Documentation menu

"Tutorial: publish an app"

The app-owner journey end to end, from self-serve registration through the dev group, subdomain and external-resource pins, to handing it to a publisher.

You have built something and want it in the Atrium directory. This walks the whole path in order, naming the exact page or route at each step.

Registration is open to everyone. The gate sits one step later, at rollout, so nothing here needs an admin until the final step.

1. Register

Three doors, one service and one set of rules:

DoorWhere
UI/adminRegister app
APIPOST /api/apps/register with { id, name, description }
Agent / MCPthe register_app tool

The API door authenticates any signed-in user or any API key. There is no role check on registering.

id is your slug: lowercase alphanumerics and dashes, up to 80 characters. It is the handle for everything downstream, including your subdomain, so choose it deliberately.

What the server forces, regardless of what you send

This envelope is applied server-side. A client cannot opt out of it by passing extra fields:

FieldForced toWhy
visibilitySECRETThe app appears in your launcher and nobody else's
syncZitadelfalseNo SSO project is created, so nothing lands in the org IdP unreviewed
provisionDevGrouptrueYou get a working dev group immediately
connector togglesllm-gateway on; observability, tracing, source-control offThe LLM gateway needs no prerequisite from you; the others each need one you have not supplied yet

The toggles are per capability, not per vendor — observability, tracing, llm-gateway, source-control are the ones an app can be opted out of in the UI. Which provider fills a capability is org-level configuration, so swapping one observability provider for another does not reset every app's toggle.

That is why open registration is safe: a fresh app is invisible, holds no identity project, and grants nobody anything. Blast radius is zero until someone approves it.

You come out of this step with the app row, an owner row naming you, a dev group, and, when a DNS connector is connected, a best-effort auto-claimed subdomain. The claim is first-wins and is skipped silently if the name is already taken, so verify it rather than assuming.

2. Know what you can and cannot change

You are now the app's owner, a per-app role held in Atrium's own tables rather than a Zitadel role.

You canYou cannot
Edit name, description, environment URLs, contact, where-hostedChange category or card visibility
Delete the app you ownPublish it yourself
Create your own PROJECT / AUXILIARY groupsCreate a DEPARTMENT group
Manage your subdomainsRe-map the Zitadel project
Mint personal API keysToggle public access

Edit at /admin/apps/<slug>. The PATCH route behind that page accepts a bag of fields and silently drops anything above your tier, returning them in droppedFields. So a request that appears to succeed may not have applied everything you sent; check that list rather than assuming.

The same rule applies through the agent: update_app filters the patch by field tier and refuses outright if nothing survives.

3. Understand your dev group

Registration provisions a dev group, which is the thing that projects your app into the surrounding tools. What matters is not which tools, but that a sync target runs in one of two modes, and the chips on the group card say which:

  • A live chip means "belong to this group and you have access there, now" — the membership change is written immediately.
  • A preview chip means "this app is wired here, but provisioning is a separate deliberate step, not driven by each member add". Real writes happen at app-create or a deliberate backfill.

Do not read a preview chip as working access. That is the whole point of the distinction: an infrastructure provisioner that only previews would otherwise look exactly like access you already have.

Today's targets, as an example of how the two modes land:

TargetMode
LiteLLM, Sentry, Langfuselive
Bitbucket, Dokploypreview

4. Claim a subdomain

If auto-claim did not fire, or you want a different name, claim one explicitly.

  • UI: the DNS section of /admin/apps/<slug>
  • Agent: claim_dns_subdomain, with get_dns_targets to see the zones and open namespace first, and list_dns_records to see what you already hold

Claims are first-wins and records Atrium does not manage are hard-protected. The full model is on DNS and subdomains.

5. Pin existing resources instead of minting duplicates

This is the step most people miss, and it is the one that prevents a mess.

If your app already has a team, a project or an org at one of these vendors that predates Atrium, pin it. The reconciler then adopts the existing resource, matching it and maintaining permissions, instead of minting a near-duplicate alongside it.

What you may pin is a resource kind — the vendor-side noun a connector owns (team, project, org). An imported connector declares its kinds in the manifest's resources block, including whether a kind is minted once per app or once per stack. Today's built-in kinds:

TargetKinds you can pin
LiteLLMteam
Sentryteam, project (per-stack)
Langfuseorg
Bitbucketproject
Dokployproject

The Sentry project pin is the only per-stack one today; everything else is stack-less.

Discover what is adoptable with discover_external_resources, then pin with set_external_link, inspect with list_external_links, and remove with delete_external_link. Atrium also records provenance when a reconciler creates a resource, so teardown knows what it minted versus what it adopted.

One honesty note: these four tools sit in the admin tier, so as a plain app owner you will need an admin to run the pin for you. Do it before the first reconcile if you can, because adopting after a duplicate exists is more work than adopting before.

6. Hand it to a publisher

You cannot publish your own app. Ask an app publisher, whose entire reason for existing is that approvals should not queue behind the handful of instance admins.

Publishing flips visibility from SECRET to DISCOVERABLE. It does not hand the publisher ownership, and it does not change what your app is: category and public access stay admin-only precisely so approving is not the same as rewriting.

The publisher's side of this, including how the role is granted through a group so approver rights track the org chart, is on Publishing apps.

Checklist

  • Registered, slug chosen deliberately
  • Basics filled in at /admin/apps/<slug>, droppedFields checked
  • Dev-group sync targets understood, live versus preview
  • Subdomain claimed and verified
  • Pre-existing external resources pinned, not duplicated
  • Sent to a publisher for rollout