Ship your product into Sovereign Mode.

You bring three artifacts. The pipeline returns an attested, contained deployment your buyer’s security team can verify without trusting you or Ember.

WHAT YOU BRING

  • A digest-pinned container image. name@sha256:… only; a tag can move after review, a digest cannot. The image must be linux/amd64 — on Apple Silicon that means docker buildx build --platform linux/amd64, or the CVM refuses to boot it. It must be pullable without credentials (see PUSHING YOUR IMAGE below), and it must boot straight to serving with env-only configuration — there is no command or args field, so an entrypoint that needs a subcommand means rebuilding the image.
  • An env index card. Plain settings go in env as name-value pairs — and those values are public: they are baked into the measured config your buyer verifies, so nothing sensitive belongs there. Secrets go in secretEnv as names only, never values — the values are read from your shell env at deploy and sealed to the enclave. A secret value written anywhere in ember.json is a secret disclosed.
  • An egress manifest. The exact hostnames your product may reach, port 443 only. No wildcards, no IP literals. An empty list ("egress": []) is valid: the deployment gets no network exit at all — it answers on its ports and reaches nothing.

The whole submission is one file, ember.json:

{
  "name": "acme-rag",
  "spec": {
    "image":     "ghcr.io/acme/rag@sha256:9f2c…",
    "env":       { "MODEL": "claude-sonnet-5" },
    "secretEnv": ["ANTHROPIC_API_KEY"],
    "ports":     [8080],
    "egress":    [{ "host": "api.anthropic.com", "port": 443 }],
    "resources": { "cpus": 8, "memoryMb": 16384, "diskGb": 100 }
  }
}

ports lists where your container serves; each one is published at an attested HTTPS endpoint on the gateway (port 80/443 at https://<app-id>.<gateway>, other ports with a -<port> suffix). An optional storagePath mounts a persistent volume at that path.

resources sizes the confidential VM and is optional — omit it and you get the smallest tier: 1 vCPU / 2 GB RAM / 20 GB disk (fine for a typical single-container web app — a paid CVM bills hourly whether or not it’s busy, so the default errs cheap). Size for your whole stack: the VM runs your single image plus the policy proxy, and the disk holds your image, model weights, and data. Limits today: 16 cpus, 32768 memoryMb, 200 diskGb (need more? ask). Sizing is set at create and is not part of the measurement — changing it later means a new deployment.

WHAT THE PIPELINE DOES

The pipeline renders a contained deployment: your image runs in an attested confidential VM whose only network exit is a policy proxy — with an empty manifest, no exit is rendered at all — and your egress manifest is inlined into the deployment measurement. That measurement is approved on-chain by the governing smart contract account before anything boots. Secrets are sealed; silicon↔chain evidence is captured.

A config edit cannot widen your access. Any change to the image or egress list produces a new measurement, which requires a new on-chain approval.

GET SET UP

  • Create your org. In the console, pick CREATE ACCOUNT, enter your work email and an org name — a code lands in your inbox and signing in creates the org.
  • Register a security key. In the console, add a passkey. It becomes the owner of your org’s smart account — deployments are refused until one exists, and every release approval is signed with it.
  • Install the CLI and sign in from your shell.
npm install -g @ember-sovereignty/provisioner
export EMBER_API=https://provisioner-production-b5b7.up.railway.app
ember login                          # prints a URL + code; approve it in the console

ember login leaves an org credential at ~/.config/ember/credentials.json. Revoking that key in the console cuts the CLI’s access with it.

If your org pins a registry cooldown (npm’s min-release-age), a fresh CLI release can sit inside the window (npm error No versions available); install the exact tarball instead — npm view reads the registry directly, so it always names the latest one: npm install -g "$(npm view @ember-sovereignty/provisioner dist.tarball)"

GIVE YOUR AGENT THE SKILL

Everything on this page is written up as an agent skill — the ember.json schema, the env-vs-secrets rule, the command sequences, the debugging playbook. If you work with Claude Code, install it once and your agent knows the whole pipeline in every project:

mkdir -p ~/.claude/skills/ember-deploy
curl -fsSL https://embersovereignty.com/onboarding/SKILL.md -o ~/.claude/skills/ember-deploy/SKILL.md

That’s a personal skill — it rides along in every repo you open. To pin it to one project instead, put the same file at .claude/skills/ember-deploy/SKILL.md in the repo. Re-run the curl any time to pick up the latest version, or just read it yourself: SKILL.md →

PUSHING YOUR IMAGE

The deployment pipeline pulls your image anonymously, by digest. If you don’t already have an org registry with public pulls, use one of these — both are free for public images and take minutes to set up:

  • GitHub Container Registrydocker login ghcr.io with a personal access token that has write:packages, push ghcr.io/you/app:tag, then set the package’s visibility to public in its settings (new packages default to private, which the pipeline can’t pull). Know the trap: the visibility toggle is web-UI-only — a registry token can push but cannot flip it, and there is no API. If all you hold is a token, publish a seed tag from a GitHub Actions workflow in a public repo using the workflow’s own GITHUB_TOKEN — a package first published that way inherits the repo’s public visibility — then push your real image to the same name with your token.
  • Docker Hub — a public repository under your account; docker login, push, done.

Avoid throwaway registries with expiring tags (ttl.sh and friends): your release is pinned to the digest, and when the image expires — ttl.sh deletes after 24 hours — the pinned digest becomes unpullable and the deployment can no longer be recreated. Private registries via sealed pull credentials are on the roadmap; today the image must be public.

The push prints the digest to pin (sha256:…); you can always re-read it with docker buildx imagetools inspect ghcr.io/you/app:tag — which also confirms the manifest carries linux/amd64.

THE WALKTHROUGH

Onboarding runs through the ember CLI:

ember init                          # scaffold ember.json, the index card
ember deploy                        # secrets from your shell env, sealed
ember status                        # state + attestation evidence
ember egress add api.example.com    # a governed ceremony, not a config edit
ember release name@sha256:…         # a new version, the same ceremony
ember verify                        # quote + chain, trusting no one

ember deploy reads ember.json from the current directory, takes secret values from your shell env, and boots the first version. Anything your image fetches at runtime — model weights included — must be on the egress manifest or the proxy denies it.

The create ceremony answers in a minute or two; a large image then keeps pulling and booting for tens of minutes while the CLI follows the status (Ctrl-C is safe — ember status resumes the view). A dropped connection can’t strand a deploy: rerun ember deploy and it resumes tracking the ceremony that already ran, by name. Names are lowercase-kebab, 5–40 characters; ember delete <id> removes a record that never provisioned.

Each port you declare is published at an attested HTTPS endpoint on the confidential-VM gateway: port 80/443 at https://<app-id>.<gateway>, other ports with a -<port> suffix. The exact URLs appear on the deployment in the console’s Releases view and in ember status once the app is running — that’s the address you smoke-test and hand to your buyer.

One expectation worth setting: the first deploy’s measurement is allowlisted as part of creation — the signing ceremony starts with your first change.

Env changes ride a release too. The whole ember.json spec is re-measured on every governed change, so to change plain env or secret-env names, edit ember.json and ship it with your next ember release (the CLI has no env-only verb yet, and a release needs a new digest — rebuilding the same image with a bumped label is enough). Secret values are the exception twice over: they are sealed from your shell at deploy and never re-read, so rotating one is not yet self-serve — contact us.

ember release and ember egress add return pending-approval — nothing changes until the ceremony completes. In the console’s Releases view you sign the endorsement and approve the new measurement with your security key (gas is sponsored; you fund nothing). The pipeline commits the moment the approval lands on-chain. When the buyer’s institution holds the governing account, the same ceremony runs with their approval instead of yours.

Your security key is that ceremony’s signing authority, and nobody — Ember included — can recover it if it’s lost. Register it in a synced passkey provider (iCloud Keychain, Google Password Manager, a password manager) so losing a device never means losing the key; a 1-of-1 org with a lost key can no longer approve releases.

NO RUNTIME LOGS — DEBUG LOCALLY FIRST

Runtime logs are unavailable by design — nothing readable leaves the enclave, so there is no ember logs today (a bounded, off-by-default version is on the roadmap). Between “running” and a correct HTTP response, the only window you have is what your app serves. Budget for that before you deploy:

  • Prove health locally first. Run the exact digest-pinned image with docker run — same env names, same entrypoint — and confirm it boots to serving on its own. If it doesn’t serve locally, it won’t serve in the CVM, and there you won’t see why. Exercise every route you’ll actually demo, not just the landing page — a page that renders can hide an API route that crashes on first use (a production-bundle-only failure is the classic shape), and each fix you discover in production costs a full release ceremony.
  • Give yourself a health endpoint. A route that reports readiness and dependency state (“index synced: n docs”) is the difference between a slow first sync and a silent failure being distinguishable in one request or not at all.
  • Know what each tool answers. ember status is deployment state plus attestation evidence — it says the enclave is running the approved measurement, not that your app inside it is healthy. ember verify checks quote and chain. App health is only ever the attested HTTPS endpoint itself. And remember egress is fail-closed: a dependency missing from the manifest looks like a hang from inside, so check the manifest before suspecting a crash.
  • Env reaches PID 1 — make sure it reaches everything else. Plain and sealed env are delivered to your container’s entrypoint. If that entrypoint is a process manager (supervisord, s6, a shell script spawning services), do not assume children inherit the environment the way your local docker run suggested — in the CVM runtime that inheritance is where multi-service bundles most often break, and it fails as a silent crash-loop you can’t see. Have PID 1 capture its env to a file first (e.g. export -p > /run/app.env) and have every service source it before starting. Cheap locally, and it saves a blind release ceremony per guess in production.

IF YOUR IMAGE DOESN’T FIT YET

The one-image, env-only model is deliberate — it’s what makes the measurement meaningful — and most real products need a small adaptation to meet it. The common shapes:

  • Entrypoint needs a subcommand or flags (a CLI-style image). Rebuild with the serving command baked in: FROM your-image + ENTRYPOINT ["your-bin","serve"], push it somewhere publicly pullable (see PUSHING YOUR IMAGE above), and pin the new digest.
  • Config file required. Bake the file into the rebuilt image; keep secrets out of it — secret values ride secretEnv and are sealed to the enclave.
  • Needs a database or a second service. One image per deployment. If the backend speaks HTTPS, point the app at a managed instance over 443 and put that host on the egress manifest. If it speaks its own port (PostgreSQL’s 5432 will never fit a 443-only manifest), bundle the store inside the image — base it on the store’s image, add your binary and a start script; the bundle is one image, and the bundle is what gets measured. Check the pinned version’s actual runtime requirements before you build: a dependency that insists on its own backing store is the most common late surprise.

PROVE IT, DON’T PROMISE IT

ember verify fetches the live attestation quote, hashes the measured workload it carries, and compares that hash to the approved-measurement list on the blockchain — the chain read is direct, trusting neither your infrastructure nor Ember’s records. By default the quote comes from the deployment record; for a fully independent check, set PHALA_CLOUD_API_KEY (a free Phala Cloud account) and the quote is fetched live from the CVM platform instead.

The full method is in the technical whitepaper → If a CAIQ-style questionnaire is in your deal thread, we’ve answered it →

START ONBOARDING

Bring the image you’d ship and the hosts it needs — the steps above are self-serve, end to end. Want a guided first deployment instead? A briefing gets you one.

Request a briefing