RFC-148: Local Forge Storage prototype for KVS and SQL

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!

Prototype status

This is a prototype shared through an RFC for feedback only. It is not an EAP, beta, preview, or GA feature, and it should not be treated as a production-ready capability.

Use only non-production apps and data.

Project summary

As a follow up to RFC-128: Local development mocks for Forge Storage, we are developing a Docker-backed local development experience for Forge Storage. This RFC goes through what problems we’re looking to improve in the local development experience, how we propose to do it and where we would like your feedback

We are committed to releasing this capability, and so the prototype we’re releasing is not a proof of concept but a validation that what is built will help solve the problems we’ve identified for you or if further iteration is required.

  • Publish: Sep 24, 2026

  • Discuss: Oct 8, 2026

  • Resolve: Oct 15, 2026

Why we are doing this

For development and testing, Forge apps that use Forge Storage can have slower feedback loops, more work in maintaining test data, and require CI suites to provision and interact with real Forge Environments when production-like infrastructure is not needed

In our previous RFC, the feedback we got helped to define the following requirements for an improved development experience:

  • Local mocks for Forge KVS and SQL, usable in tunnel mode

  • Strong API parity (pagination, query behaviour, SQL syntax/dialect)

  • Data seeding and reset to support repeatable tests

  • Direct SQL access through a MySQL client to inspect and manage local test data

We have used that feedback to build a working prototype. We want to understand if what we have built, fits well into your existing workflows and CI environments.

Prototype capabilities

The prototype includes an experimental Forge CLI release and experimental versions of the two Forge Storage SDKs:

  • @forge/kvs, covering Key-Value Store, Custom Entity Store, and Secret Store;

  • @forge/sql, covering Forge SQL.

Existing calls through these SDKs are routed to local emulators when the local-storage tunnel is enabled. Developers should not need to add separate mock implementations or change their application logic.

The forge tunnel command now has a new argument for enabling local-storage which does the following:

  • Reads the app manifest to determine which storage services are required;

  • Automatically pulls missing public emulator images;

  • Starts only the required Docker services and waits for readiness;

  • Creates an isolated, deterministic namespace for the app;

  • Routes supported SDK calls to that namespace during a Forge tunnel;

  • Preserves local data between normal stop/start cycles;

  • Provides commands to seed, inspect the status of, reset, and stop local storage.

No manual docker pull should be required during the normal workflow.

Developers will not need to submit an app ID or request allowlisting to access the public prototype. The experimental CLI, SDK packages, and emulator images will be publicly accessible using the documented setup flow.

Try the prototype

Prerequisites

  • A Forge app using KVS and/or Forge SQL.

  • A supported Forge Node.js environment.

  • Docker Desktop or another compatible Docker Engine with Docker Compose v2.

Install the experimental CLI and SDKs

Install the experimental Forge CLI produced by our prototype release pipeline:

npm install -g @forge/cli@14.1.0-next.1-experimental-13c2f8d

From the Forge app directory, install the experimental SDK package or packages used by the app:

npm install @forge/api@8.1.1-next.0-experimental-13c2f8d
@forge/bridge@7.0.1-next.0-experimental-13c2f8d
@forge/bundler@7.2.3-next.1-experimental-13c2f8d
@forge/cache@2.0.7-next.0-experimental-13c2f8d
@forge/cli-shared@9.7.0-next.1-experimental-13c2f8d
@forge/csp@6.3.1-experimental-13c2f8d
@forge/dashboards-bridge@1.3.1-next.1-experimental-13c2f8d
@forge/events@3.0.7-next.0-experimental-13c2f8d
@forge/feature-flags@2.0.7-next.0-experimental-13c2f8d
@forge/feature-flags-node@4.0.7-next.0-experimental-13c2f8d
@forge/hooks@1.2.1-next.1-experimental-13c2f8d
@forge/jira-bridge@1.2.0-next.0-experimental-13c2f8d
@forge/kvs@2.0.7-next.0-experimental-13c2f8d
@forge/lint@6.2.3-next.1-experimental-13c2f8d
@forge/llm@1.0.7-next.0-experimental-13c2f8d
@forge/manifest@13.5.1-next.0-experimental-13c2f8d
@forge/metrics@1.0.7-next.0-experimental-13c2f8d
@forge/migrations@3.0.7-next.0-experimental-13c2f8d
@forge/notification@1.0.7-next.0-experimental-13c2f8d
@forge/object-store@3.0.1-next.0-experimental-13c2f8d
@forge/os@4.0.6-next.0-experimental-13c2f8d
@forge/react@12.3.0-next.1-experimental-13c2f8d
@forge/sql@4.0.7-next.0-experimental-13c2f8d
@forge/teamwork-graph@5.2.1-next.0-experimental-13c2f8d
@forge/tunnel@7.1.4-next.1-experimental-13c2f8d

Equivalent Yarn commands can be used. Direct dependencies on other @forge/* packages may also need to use versions from the same experimental release.

Use local storage with Forge tunnel

From the app directory, run:

forge tunnel --local-storage

The CLI will pull missing images, start the manifest-configured storage services, wait for them to be ready, and route normal @forge/kvs and @forge/sql calls to local storage. Without --local-storage, Forge tunnel behaviour remains unchanged.

Please exercise your normal app workflows through the tunnel. We particularly want to understand whether this feels like a drop-in development experience and whether anything would prevent you from adopting it.

Manage local storage directly

The prototype also provides standalone commands:

forge storage local start
forge storage local status
forge storage local seed --kvs ./kvs-seed.json
forge storage local seed --sql ./sql-seed.sql
forge storage local reset --kvs
forge storage local reset --sql
forge storage local reset
forge storage local stop

  • start starts the services required by the current manifest and waits for readiness.

  • status reports the configured services and their local runtime health.

  • seed loads repeatable KVS/Custom Entity/Secret Store or SQL test data.

  • reset clears one configured storage offering, or all configured offerings when no flag is given.

  • stop stops the containers while preserving local data for a later start.

We expect these commands could be useful both during local development and in CI.

An example CI flow is:

forge storage local start
forge storage local seed --kvs ./test-data/kvs.json --sql ./test-data/sql.sql
# Run the app's integration or end-to-end tests.
forge storage local stop

Current boundaries

The local emulators aim for SDK and query compatibility, but they are not a complete production simulation. The first release is not intending to reproduce:

  • Production rate limits or connection limits;

  • Production latency or performance characteristics;

  • Fault/error injection;

  • All platform permission and authentication checks;

  • Production durability or security guarantees.

Only products that are in GA are covered. The emulator images will be publicly pullable for the prototype, but making the emulator source code open source is not part of this proposal.

Testing against a real Forge environment will remain important for production confidence. Local storage is intended to make the development and automated-test loop faster and easier, not to replace every hosted integration test.

Feedback we are asking for

Please try the prototype, and let us know your feedback in this thread. We are particularly interested to hear about:

  1. What worked well? Which parts of the prototype were most useful, intuitive, or reliable in your workflow? What, if anything, is missing before you could rely on it? Please be specific about the features or commands where you can.

  2. What would stop you from adopting this? We have tested it to the best of our ability, but developers often have more complex and larger apps than the ones available to us. What would you want changed or improved in this experience? Please call out any difference that would prevent meaningful development or testing.

  3. Does tunnelling work naturally? Did forge tunnel --local-storage work with your existing application code and SDK usage?

  4. Where do the standalone commands fit? Which of start, status, seed, reset, and stop would you use during local development? Which would you use in CI?

  5. Does it fit your CI pipeline? If not, what capabilities, configuration, output, or lifecycle behaviour would need to be added?

To help us compare feedback across different workflows, please include:

  • Which storage surfaces you tested: KVS, Custom Entity Store, Secret Store, and/or Forge SQL;

  • Whether you tested locally, in CI, or both;

  • Your Docker environment and CI provider where relevant.

Do not share credentials, application data, or other secrets.

Please share feedback in this RFC thread first so other developers can react, compare workflows, and add context. If a deeper walkthrough would help, use the scheduling link in the post to book time with the team.

What happens after the RFC

The feedback period will remain open for one week. During that time, we will answer questions and use the thread to understand whether reported gaps are adoption blockers or follow-up improvements.

At the end of the week, we will summarise the feedback and close the RFC thread. Based on the quality, depth, and sentiment of the feedback, we will determine whether this capability can progress directly toward a Preview release or if an intermediate formal EAP is needed for deeper validation.

Sounds great :tada::rocket:

Is there a way to run the standalone commands and simply start a unit test of one file that has the forge/KVS import at the top and point that to the local mock without the full forge tunnel?

That is what my basic mock implementation based on pouchdb currently provides. That runs well in our CI inside GitHub actions.

@LukeD thank you for this RFC and for the efforts in improving the Forge developer experience.

Although I realise I may be repeating myself, I will continue pointing Atlassian to the firebase CLI, which I think is a really good example of a smooth DX, especially when it comes to local emulators.

IMHO, the question whether or not local emulation is enabled is not something that should be dependent on command flags. This is absolutely something that should live inside a configuration file, being either forge.json or a separate section of package.json.

The firebase CLI uses firebase.json which allows for the configuration of a lot of different options, but also the configuration of emulators:

"emulators": {
    "functions": {
      "port": 6001
    },
    "hosting": {
      "port": 6000
    },
    "pubsub": {
      "port": 8085
    },
  }

If an emulator is listed in the configuration, the CLI will automatically spin up the emulator and makes sure that tunnelled requests are routed to the emulated resource.

The firebase emulator suite includes local emulators for storage, database (Firestore), events (pubsub), hosting and functions, amongst others.

I think this really works well and will improve developer experience when also applied to Forge.

PS: one week is a very short period for asking feedback… and it makes it seem like this RFC is an afterthought and that Atlassian is basically already half way implementing this and suddenly realised it might be a good idea to ask the community. Please consider giving partners more time to engage.

I’m not saying you shouldn’t do it, but I don’t think we would use it.

Our apps run in Forge and in Jira/Confluence: if we want to test them in isolation, the best we can do is write unit tests and hope our mocks are correct.
If we want to test them within the host platform, we don’t want to mock anything: running in a tunnel is a necessary evil to get a decent hot-reloading time even if it means that we aren’t exactly mirroring the production setup. Adding a mocked storage that doesn’t replicate the limits that customers will face does not help and reduces our confidence in these supposedly end-to-end tests.

The problem of setting up the preconditions and seed data is mostly solved: we can simply have a webtrigger on our development apps and comment it out in prod.

@LukeD sounds great! How can we use it with Forge Remote / Forge Containers via a local REST API?

Hi clouless,

This is partly supported we turn on an environment variable called FORGE_LOCAL_STORAGE to a value of 1 which will direct traffic to the local emulators.

Are you spinning up a seperate image that has a server with pouchdb setup to listen to requests from a mock?

Hello Remie,

Thanks for your detailed feedback and the Firebase CLI example.

Would you expect this configuration to be used only in conjunction with Atlassian-provided emulators, or would you also expect to be able to provide a host port/URL for your own custom emulator?

It was not our intention to come off as an afterthought. We did an initial RFC on this topic (RFC-128) with a 2 week timeframe to gather ideas and thoughts from the community and continued to extend as more feedback came in.

Our goal was to have a followup to see if we had met the brief. We will be happy to keep to the same process as the previous RFC, extending this RFC if we continue to get more feedback.

Regarding the RFC timelines, we have discussed internally and have decided to extend the deadlines to hear out more information by an extra week to discuss and then an additional week to resolve.

Hey Paolo,

So our original goal as part of RFC-128 was to focus on local development but not full production parity when it comes to configuration + limits.

To confirm are you saying from your point of view that what you need most in your development loop is test your changes against a production like environment so you can have confidence your not breaching any limits? Is there any particular limits that are of most concern when making a change that you can’t be sure you’re not going to break?

@LukeD , This reads like a really good improvement to the developer experience. Also coming from Firebase we’ve seen what good DX can be on the local emulator front.

I read that that data will be kept between starting and stopping, this is great. If I compare this with Firebase, they have separate command line arguments for configuring this exporting --import=./target/firebase --export-on-exit. Not saying this is required, but Firebase allows us to configure this ourselves.

Something that’s missing from this RFC or I’m not reading it is a UI that allows us to read through the different kinds of storage options. Currently the Forge developer console is really limited in what can be read in terms of data from development accounts. Custom Entities are not supported at all in the developer console. The SQL viewer is really limited in the amount of data / data types that can be read.

I understand that you will not be replicating production limits, but the limits in Forge play a big role in building out an app. Firebase emulators always have given us the certainty that if it works during development it works in production.

As for CI testing. We currently already spin up a local MySQL instance to be able to test/mock. Having this work through the forge cli already would be great.

If I’m running the app in forge tunnel, it will still use the Atlassian APIs for everything else: it’s not going to run with mocks for e.g. the work item APIs.
If we had an official fully mocked environment (à la Localstack), sure, I could see the value of this. Since we don’t, I don’t see the advantage of using mock KVS and SQL when everything else will be running with the Atlassian services anyway.

For us it would be much better to e.g.

  • fix all the vulnerabilities
  • add more structured JSON output for scripting, like for forge variables

in forge cli instead of adding more and more things.

Ok cool.

I actually use a in-memory pouchdb setup where in my vitest test the seeding is made before the test without any additional docker images or external servers. Simply a in memory drop in replacement for forge storage api. (With very limited functionality. Add/Delete/Update/Fetch, but no sorting other other query functionality)

As long as I can integrate it into GitHub Actions as service containers I am fine with any solution. I would need to define a GitHub Service container aka “docker inside GitHub action” like so.

Here is for example how I spin up Confluence with a dockerized db inside my DC pipeline:

  test:
    needs: preparation
    runs-on:
      group: codeclou-large-runners
      labels: ubuntu-latest-m
    container:
      # For Confluence 10 jdk21
      image: codeclou/infrastructure-github-actions-docker-atlassian-sdk:jdk21-atlassian9.1.1-v17
      ...
    services:
      confluencedb:
        image: postgres:17.7-alpine
        env:
           ...
        ports:
          - 5432:5432
        options: --health-cmd=pg_isready --health-interval=10s --health-timeout=10s --health-retries=10
      confluence:
        image: ...
    steps:
      - name: run some tests against confluence
        run: test.sh

GitHub will make the hostname `confluencedb` available such as the service name and map the ports accordingly. If we can configure it like so, it would be great.

Hi Ben,

We haven’t tested with Forge remote explicitly, would you expect to be able to start the emulator independently and set up the mock routing yourself, or do you imagine a different experience?

We haven’t considered Containers yet as part of our initial release as it’s still in preview but it is something we are wanting to make sure is properly supported as part of future iterations.

Hi Jannick,

That’s good feedback around giving more control around data persistence after the emulator has been stopped, it’s something we can look into.

As part of the first iteration there is no currently planned UI component but something we can look into. Do you have any examples with other tools that you have found provide a good UI for local storage viewing?

I agree that the observability in dev console for storage is quite limited in regards to what can be seen for our capabilities. We are looking into how we can have a better experience that will provide a full view of storage and what’s contained, so it’s easier to run apps using storage on the platform. I don’t have dates at the moment but it is something we are aware of and are investigating.

We’ll take onboard the feedback around the importance of being able to test against the limits of production environment.

That makes sense, so from your point of view there’s no point having a partial mock because you still have to deal with running against real Atlassian services for all other parts which means you don’t have the true value of a mocked environment.

Hey Marc,

I’m from the Storage team and something we’re wanting to ship for storage experience rather than something being driven by the team that owns the CLI. I will raise with that team your feedback on areas that could use improvement, and ensuring we have patterns in place to make sure we have quick vuln resolution across CLI and other packages from Forge.

Hi @LukeD ,
Thanks for your response!

Hi @LukeD ,

I only really have experience with the Firebase. It’s Firestore emulator UI allows me to view each document. I expect something similar could be made for KVS related storage where we have the ability to view every stored key.

For SQL I see the following is mentioned. So for SQL if we could spin up a viewer and connect it that would be great.

Yes, we’d expect to be able to start the Local Forge Storage by ourselves (or through a forge CLI command) and use some kind of REST API to interact with it. We’d then be able to change the REST API URLs from the production ones to (probably) localhost to be able to run our Forge Storage tests locally.