The whole picture
Here is the sequence in plain words.- You get access, once. The first time you ship to a platform, you run
aomi-git request --email <email> --git-account <github-user>. Ops invite your GitHub account to the platform repo and email you a per-app activation code. This step runs once, not per deploy. - You author your App in your own source repo: a Rust
cdylibcrate plus anaomi.toml. - You deploy with
aomi-git deploy. It copies a snapshot of your source into a clone of the platform repo (community-apps), commits, and pushes to thepublishbranch. - CI runs in the platform repo. It builds your cdylib and cuts a GitHub release tagged
apps-<slug>-<short-commit>. - You activate with
aomi-git activate, setting your per-app code asAOMI_APP_ACTIVATION_TOKEN. Your code is scoped to this one App, so it can never touch any other. - The backend hot loads your release. It fetches the tarball, validates it, and swaps your App into the live catalog. No restart, no downtime.
Your aomi.toml deploy fields
Your aomi.toml is the contract that travels with your App. Every field below feeds the deploy plan and the release. Here is each one.
| Field | What it does |
|---|---|
name | Your slug. Use kebab-case. This becomes the release tag apps-<name>-<short-commit>. |
display_name | The label shown to people in the backend registry. |
platform | The platform tag. Must be community for the community-apps repo. Nothing resolves without it. |
git | The platform repo location. For community apps this is https://github.com/aomi-labs/community-apps. If you leave it out, the backend’s platform record supplies it. |
public | Whether your App is visible to all backend users. true for community apps. |
server_tags | Which backend tiers may load this release. Omit to default to ["staging"]. Set to ["prod"] or ["staging", "prod"] once tested. This is a contract, not advice: ops can narrow your scope but can never widen it. |
access_token | A GitHub token used only by private platform repos. See the note below. |
About access_token
The community-apps repo is public. The backend can fetch your release tarball from its GitHub releases page without any credentials. So for a public community App you omit access_token entirely. You do not need one.
Private platform repos are different. A repo like krexa-hosted-apps needs a GitHub token with read access to releases. When you need it, you declare it as a reference to an environment variable, never as the token itself.
aomi-git deploy
You run aomi-git deploy from your source repo, the crate that holds your aomi.toml and src/lib.rs. It never edits your source. It copies a snapshot into a clone of the platform repo and pushes from there.
Dry run first
Always dry run before a real deploy. A dry run computes the full plan, runs the online checks, writes.aomi/deployment.json, and pushes nothing. Point it at staging so it also checks that the backend is reachable.
If any stage fails, fix the underlying issue, usually a field in your
aomi.toml, before you run the real deploy. A [warn] line is advisory and does not block. The common one is git_url_matches_platform when you deploy from a fork.
The real deploy
When the dry run is clean, deploy for real.- Snapshots your source tree into the clone under
apps/<slug>/. - Writes
apps/<slug>/.aomi/deployment.json, the build contract CI reads. - Commits and pushes to the
publishbranch. - The community-apps CI fires automatically: it validates the staged source against
deployment.json, runscargo build --releasefor the cdylib, and uploads a release tarball under the tagapps-<slug>-<short-commit>.
The managed transit cache vs the escape hatch
By defaultaomi-git manages a clone of community-apps for you under ~/.aomi/transit/aomi-labs-community-apps/. You never touch it. On the first deploy it clones; on later deploys it fetches and resets. Auth flows through your normal git credential helper, so if gh auth login works, this works.
If you need to manage the clone yourself, for CI with no network, custom auth, or to inspect the staged tree before it pushes, pass your own directory with --platform-dir.
origin/publish. Most contributors never need this.
Other useful flags
| Flag | What it does |
|---|---|
--dry-run | Plan plus online checks. No staging, no push, no activation. Refreshes .aomi/deployment.json. |
--allow-dirty | Permit an uncommitted working tree in the plan and during staging. Use it only when you know why your tree is dirty. |
--platform-dir <DIR> | Stage and push from a clone you manage yourself, skipping the transit cache. |
--json | Print the plan or outcome as JSON instead of the human summary. Useful in scripts. |
--platform <NAME> | Override the platform tag. Defaults to your aomi.toml value, then community. |
The validation pipeline, stage by stage
Every deploy, including a dry run, runs a validation pipeline and records the result in.aomi/deployment.json. The pipeline has four ordered stages. Each stage is a precondition for the next, so a failing gate stops the rest and the downstream stages are recorded as skipped.
aomi.toml, so they run even with no network. Stages 3 and 4 are online. They run only when a backend URL is available, which is why you point the dry run at staging.
In short: stages 1 and 2 check that your files are ready, and stages 3 and 4 check that the platform will accept them. If every line reads [ok], you are good to deploy. The accordion below has the detail when you need it.
What each stage checks
What each stage checks
Stage 1, workspace. Asks: is the local tree shippable? It runs one check,
git_clean, which fails if you have uncommitted changes. A failure means commit or stash first. CI builds from a clean source commit, so a dirty tree is a hard stop unless you pass --allow-dirty.Stage 2, manifest. Asks: does aomi.toml declare what we need? It runs platform_declared, a check at error severity that fails if [app].platform is missing, since nothing resolves without it. It also runs git_declared, a check at warn severity for [app].git; a missing git URL only skips the later git_url_matches_platform check, so it does not block you. This stage also resolves your server_tags and records whether you set them or the default ["staging"] filled in.Stage 3, platform. Asks: can we resolve the declared platform repo and its deploy branch? It runs backend_reachable (the gate that opens stages 3 and 4, passing when GET /api/control/platforms succeeds), platform_resolved (your platform is registered with the backend), and branch_matches_contract (your target branch equals the platform’s contractual deployment_branch, which is publish). A branch_matches_contract failure means your push would not deploy on its own. It also runs git_url_matches_platform as a warn, so a fork passes with an advisory note.Stage 4, backend. Asks: will the backend actually accept this release? It runs server_tags_subset, which fails if your server_tags are not a subset of the backend’s AOMI_SERVER_TAGS. A failure here becomes a 409 error at activate time, so it is worth catching now. Match your declared tags to the backend you plan to activate against.The backend runs one more check of its own at activation time, sdk_version_matches_host. It rejects a bundle whose pinned aomi-sdk does not equal the backend’s required_sdk_version. The deploy preflight cannot see this, since the version lives in the built release, so pin aomi-sdk to the platform’s required_sdk_version (see platform.json) before you deploy.Each check carries a severity. An error check is a gate: if it fails, the stage fails and the deploy is blocked. A warn check is advisory: if it fails, the stage is downgraded to warning but you are not blocked. A stage that never runs because an upstream gate failed, or because it had no backend URL, is recorded as skipped.aomi-git status
After a deploy, run aomi-git status from your source repo to see when CI finishes, when the release asset exists, and whether the backend has loaded your App. It reads your .aomi/deployment.json and polls GitHub for you, rolling both signals into one report.
ci line turns to [ok] green - Publish Aomi Apps and release shows it is published and ready to activate.
You are ready to activate when ci shows green and release shows it is published and ready to activate. Pass a specific tag with aomi-git status apps-<slug>-<commit> to check one release; otherwise it uses the latest deploy’s tag.
You can still watch the raw pages if you prefer: the Actions tab for CI and the release tag page for the asset. aomi-git status simply rolls both up.
Activation
You activate your own App. You hold a per-app activation code, scoped to this one App, so you runaomi-git activate yourself for every release. There is no per-release handoff to ops.
Get access the first time
The first time you ship to a platform, runaomi-git request to ask for access. This runs once, not per deploy.
deploy push is allowed), and they email you a per-app activation code. The code arrives by email out of band; it is never part of the request and never travels over Discord.
Preview the exact message first without posting:
This
request step is community tier only. B2B partners deploy through a server-side proxy and do not get direct repo access, so they do not use request.Activate the release
Once CI is green and you hold your per-app code, activate the release yourself. Runaomi-git activate from your source repo. It reads .aomi/deployment.json and pulls the release tag, platform, source repo, display name, visibility, and target tags from it, so you pass almost nothing. Set your code as AOMI_APP_ACTIVATION_TOKEN.
https://staging-api.aomi.dev/api/control/apps/status.
Preview the request without sending any HTTP:
deployment.json is not on hand, such as turning an older tag back on from a fresh machine, pass every field explicitly.
Public and private platforms use the same flow
The flow is the same whether you ship to a public platform likecommunity or a private partner platform like krexa. You request access once, you deploy, you activate with your per-app code. The only difference is the platform repo behind the scenes and, for private repos, the access_token field described above.
Iterating
To ship a change, edit your code, commit, and runaomi-git deploy again. Each deploy produces a fresh release tagged with the new short-commit. The previous release stays on GitHub, so you always have a tag you know works to roll back to by turning it back on.
Promoting from staging to prod means you deploy again. Edit server_tags in aomi.toml to ["prod"] or ["staging", "prod"], run aomi-git deploy to cut a new release carrying the wider scope, then run aomi-git activate again against the prod backend. A release can only be activated to the tiers its own build declared, so widening to prod requires you to deploy again first. That is by design.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
aomi-git: command not found | The binary is not installed or not on your PATH. | Run cargo install --git https://github.com/aomi-labs/aomi-sdk --features cli,dev-runtime aomi-sdk, then confirm ~/.cargo/bin is on your PATH. |
aomi.toml [app].access_token must be \$ENV_VAR_NAME“ | You put a literal token in aomi.toml. | Use an env-var reference: access_token = "$YOUR_VAR_NAME". Never commit secrets. For public community apps, omit the field. |
git tree is dirty | Uncommitted files in your source repo, often .aomi/deployment.json from a previous dry run. | Commit your changes, or add .aomi/, target/, and Cargo.lock to .gitignore. Use --allow-dirty only when intentional. |
sdk_version mismatch | Your aomi-sdk dependency does not match platform.json’s required_sdk_version. | Pin the exact version: aomi-sdk = "=0.1.20". Check platform.json in community-apps for the current value. |
Cargo.toml must set [lib].crate-type = ["cdylib"] | Your crate is missing the cdylib library type. | Add a [lib] section with crate-type = ["cdylib"]. |
aomi_create returned null (CI) | Your dyn_aomi_app! macro is missing or malformed. | Compare your src/lib.rs against a working app such as apps/fanforge. |
activation endpoint returned 502 | The release tarball does not exist yet, a CI race, or the backend cannot reach GitHub. | Wait for CI to finish and the release to publish, then retry. |
activation endpoint returned 409 | Your target tags are not a subset of the backend’s AOMI_SERVER_TAGS. | Match your server_tags to the backend you are activating against. |
git clone ... exited 128 | aomi-git could not fetch the platform repo into its transit cache, an auth or network issue. | Run gh auth login. If still stuck, rm -rf ~/.aomi/transit/aomi-labs-community-apps/ and retry. |
Next
Your App is live on the backend. The natural next step is the frontend: give people a way to chat with it.Add the chat widget
Drop the React widget into a frontend so people can chat with your deployed App.
The builder toolchain
The full reference for the three Rust binaries:
aomi-build, aomi-run, and aomi-git.Common errors
The errors you are most likely to hit, each with its fix.