RFCs are a way for Atlassian to share what we’re working on with our valued developer community.
It’s a document for building shared understanding of a topic. It expresses a technical solution, but can also communicate how it should be built or even document standards. The most important aspect of an RFC is that a written specification facilitates feedback and drives consensus. It is not a tool for approving or committing to ideas, but more so a collaborative practice to shape an idea and to find serious flaws early.
Please respect our community guidelines: keep it welcoming and safe by commenting on the idea not the people (especially the author); keep it tidy by keeping on topic; empower the community by keeping comments constructive. Thanks!
Project Summary
Atlassian is planning to allow customers to easily migrate between Atlassian’s clouds, including by migrating Forge app data between clouds on behalf of app partners. This requires a way for app partners to help us find and map identifiers that change during a cloud-to-cloud migration. This RFC is focused on the best way to do that.
-
Publish: 11th November 2025
-
Discuss: Until the 25th of November 2025
-
Resolve: Estimated to be by the 5th of December 2025
Problem
Atlassian is soon going to start supporting migrations between Atlassian Clouds. Specifically, we support three clouds:
-
Atlassian Commercial Cloud - the first and most familiar cloud.
-
Atlassian Government Cloud - our FedRamp Moderate cloud.
-
Isolated Cloud - for customers in highly regulated industries who need their own cloud isolated from others.
Cloud-to-cloud migrations are migrations between these clouds - for example, if a customer needs to move from Commercial to an Isolated Cloud.
We would like to ensure that customers have a wide choice of third-party apps that they can use in any cloud, and that it is easy for app partners to retain their customers through a cloud-to-cloud migration. Cloud-to-cloud Migration therefore includes app migration in its scope.
Because Isolated Cloud customers need strict control over data leaving their cloud, we’re focusing on supporting Runs on Atlassian (RoA) Forge Apps. Cloud-to-Cloud is therefore scoped to migrating Forge Apps between clouds (with a Connect-to-Forge migration within the same cloud first, if needed).
As a general principle, Atlassian will move the data for cloud-to-cloud migrations for customers on behalf of app partners. However, we are unfortunately not able to guarantee that all identifiers for entities will remain consistent between clouds. As an App Partner writing a Runs on Atlassian Forge app, you have access to Forge Storage(SQL, Key-Value Pairs and Object Store), and might choose to store entity identifiers in Forge Storage. You might also choose to store identifiers in Forge Entity Properties. Atlassian does not dictate the format of data with app partner controlled content, and so we wouldn’t be able to automatically map this data without help from app partners. This RFC seeks your input on the best interface for providing that input to Atlassian.
Proposed Solutions
This is an early stage RFC - we want to learn what would work best for our ecosystem partners. We have two proposed solutions and would love your input on which would work best for your business. At this stage, we only provide a sketch of what each interface might look like, and will refine it further once we’ve decided which path(s) to take.
Solution A: Simpler with controlled power
The first solution would be for app partners to provide information about how Atlassian can find and substitute identifiers.
App Partners already provide a Forge manifest file, in YAML, describing their app. We’d add a new optional module that provides information needed for migrations to proceed.
App partners would provide a list of identifier locations. They’d firstly specify which SQL column or key or value the identifier resides in, and what type of identifier it is. They’d need to refrain from putting identifiers in Forge Objects (i.e. Object Store would not be supported). There would be two ways to specify how to do the transform:
-
Regular expressions (limited to the RE2 syntax - no backtracking). Every regex would include a capture group around the identifier, and every non-overlapping match would have the capture group mapped, and the string put back together.
-
JsonPath - for JSON only. The JsonPath would specify how to find the JSON node, which is then transformed.
The actual manifest would look something roughly like this:
{
...,
"modules": {
...
"migrationMappings": [
{
"type": "ForgeSQL",
"table": "mytable",
"column": "mycolumn",
"transformation": {
"namespace": "identity:user",
"subType": "jira.userkey",
"regex": "([0-9]+)(?:,|$)"
}
},
{
"type": "ForgeKVS",
"matchKey": "^user-record-.*$",
"transformKey": {
"namespace": "identity:user",
"subType": "jira.userkey",
"regex": "^user-record-(.*)$"
}
},
{
"type": "ForgeKVS",
"matchKey": "^user-record-.*$",
"transformValue": {
"namespace": "jira:issue",
"jsonpath": "$.jira.favourite-issue"
}
},
{
"type": "ForgeEntityProperty",
"entityType": "issue",
"matchKey": "^most-closely-related-issue$",
"transformValue": {
"namespace": "jira:issue",
"regex": "^\s*([^\s]+)\s*$"
}
}
]
...
}
}
We would impose an upper limit on the number of transformations allowed to be defined (i.e. how many columns, not how many rows).
There are some benefits to this simpler solution:
-
Atlassian would do all the transformations as we migrate base data for customers, rather than needing to invoke customer-defined compute to transform records. This would reduce costs for Atlassian (when below the free tier) and for app partners (when above the free tier), and allow for a much faster migration of data for customers.
-
It is likely less effort for app partners to define.
However, the downside is that app partners might encode data in Forge Storage in a format that cannot be transformed using regexes or JsonPath. For example, data could be in a binary format. App Partners could work around this problem by not using those formats, but in some cases, that might mean using more storage.
Solution B: More complex, more powerful
Alternatively, we could let app partners define a Forge function that’s invoked with a batch of records, which could call an API to retrieve mappings. App Partners would then transform and save the records into Forge Storage.
This approach would give more flexibility to app partners, at the expense of being harder to implement. For large migrations, this could mean a lot of function calls, which might get expensive for app partners (once compute is monetised).
Allowing both options
If most app partners need the simplicity, but some would benefit from the more powerful solution, we could consider implementing both, as a choice for app partners on an app-by-app basis. Atlassian would most likely prioritise the option that provides the most value to our customers first.
Asks
While we would appreciate any feedback you have to this RFC, we’re especially interested to hear:
-
Do you think Solution A would work for your app, given how you plan to use of Forge Storage?
-
Which option would you prefer to implement for your app(s)?
-
If Solution A isn’t enough, are there any extensions we could make to it that’d make it work for you, without bringing the full complexity of Solution B?
-
Do you expect your Forge app will store identifiers to Atlassian objects in any vendor controlled / free text place apart from Forge SQL, Forge KVP, and Entity Properties? If so, where else would you store them?
-
Is putting the mappings in the manifest the most convenient option for you, or would another option (e.g. editing them through the developer console) be better?