{"_id":"@ember-sovereignty/provisioner","_rev":"5-196ef7184f9b642fe802a7270ef94f80","name":"@ember-sovereignty/provisioner","dist-tags":{"latest":"0.1.1"},"versions":{"0.0.1":{"name":"@ember-sovereignty/provisioner","version":"0.0.1","license":"UNLICENSED","_id":"@ember-sovereignty/provisioner@0.0.1","maintainers":[{"name":"glitch003","email":"chris@litprotocol.com"}],"bin":{"ember":"bin/ember.mjs"},"dist":{"shasum":"4f07e4ee865e63924a3f37c5bee030b32326a771","tarball":"https://registry.npmjs.org/@ember-sovereignty/provisioner/-/provisioner-0.0.1.tgz","fileCount":19,"integrity":"sha512-7WSjp7+d84LtewvGGBOVXGQmI4/iMZtd9JpA0jfYonheYQUC58WKGWSTOnk0ds4ehkWVL1AYaGsd8tN4kkWkmQ==","signatures":[{"sig":"MEYCIQC0gSgapR76pZul0Lg2KbuyHnQj8DjoXznswDAaJkfj9AIhAJtWwJB94Ue5lP/DM1yZm9FPUW6cBy16bvbP56NUswSM","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":128832},"type":"module","engines":{"node":">=20"},"gitHead":"982519e223eb9b55b4233a9a765e45414c882ae3","scripts":{"dev":"tsx watch src/dev.ts","test":"vitest run","start":"tsx src/index.ts","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test"},"_npmUser":{"name":"glitch003","email":"chris@litprotocol.com"},"_npmVersion":"11.14.1","description":"Ember vendor provisioner — image + index card + egress manifest in, SCA-governed contained CVM out. Compose render, governance ceremony, attestation evidence, read-only console, ember CLI.","directories":{},"_nodeVersion":"22.16.0","dependencies":{"tsx":"^4.16.0","zod":"^3.23.0","viem":"^2.55.2","express":"^5.0.0","@phala/cloud":"^0.3.0","permissionless":"^0.3.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.1.0","supertest":"^7.0.0","typescript":"^5.5.0","@types/node":"^20.14.0","@types/express":"^5.0.0","@types/supertest":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/provisioner_0.0.1_1785535675004_0.004512000238497738","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@ember-sovereignty/provisioner","version":"0.0.2","license":"UNLICENSED","_id":"@ember-sovereignty/provisioner@0.0.2","maintainers":[{"name":"glitch003","email":"chris@litprotocol.com"}],"bin":{"ember":"bin/ember.mjs"},"dist":{"shasum":"ba9c9ec176f53ce56e49c69d6065e27317d79564","tarball":"https://registry.npmjs.org/@ember-sovereignty/provisioner/-/provisioner-0.0.2.tgz","fileCount":19,"integrity":"sha512-4nJKvdOuh5WIfx8nHLotGrpwEGWldsdugEim5Eem7NWvmJjFXBz3r2+v95O1KAFpx8267amX09P61SiWlLtzkw==","signatures":[{"sig":"MEUCIQDkGzVjKbIaYfhjxqMpMgA4gnK+AFplcCMtMJa9UugRNQIgEcaHD8ZHxykPdiZyIqDphyC2eLJtRMP8tt2JElmCwi0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":129812},"type":"module","engines":{"node":">=20"},"gitHead":"73bd4e15e1d56c4bd4cb0ddbe0b9e0c0956f8967","scripts":{"dev":"tsx watch src/dev.ts","test":"vitest run","start":"tsx src/index.ts","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test"},"_npmUser":{"name":"glitch003","email":"chris@litprotocol.com"},"_npmVersion":"11.14.1","description":"Ember vendor provisioner — image + index card + egress manifest in, SCA-governed contained CVM out. Compose render, governance ceremony, attestation evidence, read-only console, ember CLI.","directories":{},"_nodeVersion":"22.16.0","dependencies":{"tsx":"^4.16.0","zod":"^3.23.0","viem":"^2.55.2","express":"^5.0.0","@phala/cloud":"^0.3.0","permissionless":"^0.3.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.1.0","supertest":"^7.0.0","typescript":"^5.5.0","@types/node":"^20.14.0","@types/express":"^5.0.0","@types/supertest":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/provisioner_0.0.2_1785556679516_0.20947502002573737","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@ember-sovereignty/provisioner","version":"0.0.3","license":"UNLICENSED","_id":"@ember-sovereignty/provisioner@0.0.3","maintainers":[{"name":"glitch003","email":"chris@litprotocol.com"}],"bin":{"ember":"bin/ember.mjs"},"dist":{"shasum":"0390229cf2d3d1452a45fcb7c8b85e078b82b35a","tarball":"https://registry.npmjs.org/@ember-sovereignty/provisioner/-/provisioner-0.0.3.tgz","fileCount":19,"integrity":"sha512-zT7CSwhJieU2LxWVMygPfxJBfP4z0B3pStz4V1pbBboou+R/eiexFNRVFiXyTg94Vj3fHtLBK/7ASPiSP4FocA==","signatures":[{"sig":"MEUCIQCs/zgF9VbWpRm6V02jR8kzrAwFWv0nGXNz9GbqD9kiVAIgWeW6E/azlcFSZBipnGVB3DF31OA68deKdmZFukZUhf4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":148150},"type":"module","engines":{"node":">=20"},"gitHead":"49437f044d488bb0b3bf7789633be129fe9933c9","scripts":{"dev":"tsx watch src/dev.ts","test":"vitest run","start":"tsx src/index.ts","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test"},"_npmUser":{"name":"glitch003","email":"chris@litprotocol.com"},"_npmVersion":"11.14.1","description":"Ember vendor provisioner — image + index card + egress manifest in, SCA-governed contained CVM out. Compose render, governance ceremony, attestation evidence, read-only console, ember CLI.","directories":{},"_nodeVersion":"22.16.0","dependencies":{"tsx":"^4.16.0","zod":"^3.23.0","viem":"^2.55.2","express":"^5.0.0","@phala/cloud":"^0.3.0","permissionless":"^0.3.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.1.0","supertest":"^7.0.0","typescript":"^5.5.0","@types/node":"^20.14.0","@types/express":"^5.0.0","@types/supertest":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/provisioner_0.0.3_1785647047103_0.6140251018806167","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@ember-sovereignty/provisioner","version":"0.1.0","license":"UNLICENSED","_id":"@ember-sovereignty/provisioner@0.1.0","maintainers":[{"name":"glitch003","email":"chris@litprotocol.com"}],"bin":{"ember":"bin/ember.mjs"},"dist":{"shasum":"5950b6bc36a7390069986fb6c8971a964479d00f","tarball":"https://registry.npmjs.org/@ember-sovereignty/provisioner/-/provisioner-0.1.0.tgz","fileCount":19,"integrity":"sha512-eR9pzHzfUufJd+y8XMEnPjnSEqaO+P28qMVqc6hWxkjqKPc0Giu9B+VOyUpqrJf4XE4HhYQhsnp1LYNPl/U5Dg==","signatures":[{"sig":"MEUCIQDMzMomjz56r7f+lj8hA98UVuP0QOieQ2JS8c+SesNzZgIgJKmiWYLznBmz/aWxKLrm3pM06+4IJfQHwkhpcRJWWU8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":164140},"type":"module","engines":{"node":">=20"},"gitHead":"7aa27ea9870e52865f9443d00945825cf17b71a2","scripts":{"dev":"tsx watch src/dev.ts","test":"vitest run","start":"tsx src/index.ts","typecheck":"tsc --noEmit","prepublishOnly":"npm run typecheck && npm test"},"_npmUser":{"name":"glitch003","email":"chris@litprotocol.com"},"_npmVersion":"11.14.1","description":"Ember vendor provisioner — image + index card + egress manifest in, SCA-governed contained CVM out. Compose render, governance ceremony, attestation evidence, read-only console, ember CLI.","directories":{},"_nodeVersion":"22.16.0","dependencies":{"tsx":"^4.16.0","zod":"^3.23.0","viem":"^2.55.2","express":"^5.0.0","@phala/cloud":"^0.3.0","permissionless":"^0.3.7"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^2.1.0","supertest":"^7.0.0","typescript":"^5.5.0","@types/node":"^20.14.0","@types/express":"^5.0.0","@types/supertest":"^6.0.0"},"_npmOperationalInternal":{"tmp":"tmp/provisioner_0.1.0_1785885883382_0.4381005373786937","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@ember-sovereignty/provisioner","version":"0.1.1","type":"module","license":"UNLICENSED","publishConfig":{"access":"public"},"description":"Ember vendor provisioner — image + index card + egress manifest in, SCA-governed contained CVM out. Compose render, governance ceremony, attestation evidence, read-only console, ember CLI.","engines":{"node":">=20"},"bin":{"ember":"bin/ember.mjs"},"scripts":{"start":"tsx src/index.ts","typecheck":"tsc --noEmit","test":"vitest run","dev":"tsx watch src/dev.ts","prepublishOnly":"npm run typecheck && npm test"},"dependencies":{"@phala/cloud":"^0.3.0","express":"^5.0.0","permissionless":"^0.3.7","tsx":"^4.16.0","viem":"^2.55.2","zod":"^3.23.0"},"devDependencies":{"@types/express":"^5.0.0","@types/node":"^20.14.0","@types/supertest":"^6.0.0","supertest":"^7.0.0","typescript":"^5.5.0","vitest":"^2.1.0"},"gitHead":"e221d8956cecbd8771aeab888d5ad1efd8b411f6","_id":"@ember-sovereignty/provisioner@0.1.1","_nodeVersion":"22.16.0","_npmVersion":"11.14.1","dist":{"integrity":"sha512-wxjHSlviFoPqcl4BZMMjjF7Ju/z2li6D71OdGiVEqyJKebFwNFQygX4OvWvMXVOGmdr+cyZWoHdMWcVQsKjyNA==","shasum":"a84c21ac33d96fd78f0aa80801b2548ec7561599","tarball":"https://registry.npmjs.org/@ember-sovereignty/provisioner/-/provisioner-0.1.1.tgz","fileCount":19,"unpackedSize":204540,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIBHFtYB9BRftroP8R0R64wi5Bst5DkVXUfxG5nB6JAbxAiAp75al3YxfteFCtpt8QD1IX09PSm5xRqIRs+Z0yB1fEQ=="}]},"_npmUser":{"name":"glitch003","email":"chris@litprotocol.com"},"directories":{},"maintainers":[{"name":"glitch003","email":"chris@litprotocol.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/provisioner_0.1.1_1786493463632_0.31658204764220566"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-31T22:07:54.852Z","modified":"2026-08-12T00:11:03.936Z","0.0.1":"2026-07-31T22:07:55.198Z","0.0.2":"2026-08-01T03:57:59.655Z","0.0.3":"2026-08-02T05:04:07.262Z","0.1.0":"2026-08-04T23:24:43.525Z","0.1.1":"2026-08-12T00:11:03.770Z"},"license":"UNLICENSED","description":"Ember vendor provisioner — image + index card + egress manifest in, SCA-governed contained CVM out. Compose render, governance ceremony, attestation evidence, read-only console, ember CLI.","maintainers":[{"name":"glitch003","email":"chris@litprotocol.com"}],"readme":"# Provisioner — vendor image + index card in, governed contained CVM out\n\nImplements the vendor release pipeline (`plans/VENDOR-RELEASE-PLAN.md`,\nissue #54 records the deferred dynamic-egress variant). Generalizes the\nRAG demo's live-verified path (`demo/anythingllm/phala/`): a vendor\nsubmits a digest-pinned image, an env index card, and an egress manifest;\nthe provisioner renders the contained compose (vey proxy as sole exit,\npolicy **inlined and therefore measured**; an empty manifest renders the\nno-exit variant — see Intake rules), runs the governance ceremony\n(DstackApp on Base, SCA `addComposeHash`), deploys the CVM with sealed\nsecrets, and captures silicon↔chain evidence.\n\n## This runs locally — it is not a hosted service\n\n> **Stale as of 2026-08-03: it IS hosted.** The provisioner runs on Railway at\n> `https://provisioner-production-b5b7.up.railway.app`, and `/onboarding/` tells\n> vendors to point `EMBER_API` at it — that is how self-serve onboarding works\n> and how the Onyx and ToolHive dry runs were done. The argument below still\n> describes the real exposure (admin authority over CVM deploys and chain\n> ceremonies), and it is worth re-reading before widening that surface; it just\n> no longer describes how we operate. Note the mitigation that landed with\n> hosting: the pipeline can never sign a governance approval (P9, #82), so a\n> compromised admin token cannot approve a measurement. See\n> [`../MVP.md`](../MVP.md).\n\nThe provisioner is an npm package you run on your own machine, not\nsomething to deploy to Railway/Fly/etc. `npm run dev` (or the real-driver\nboot) starts the API, the console, and the CLI's endpoint locally; the\noperator runs it with the admin token and Phala/Base credentials in their\nshell for the duration of a provisioning ceremony, then stops it. The\nonly thing it deploys to a cloud is the **vendor's CVM** (to Phala) —\nthe provisioner itself never needs to be reachable on the public\ninternet, and hosting it there would just expose admin authority over\nCVM deployments and chain ceremonies for no benefit.\n\n```\nember CLI / read-only console\n        │\n   provisioner API  (api.ts — vendor bearer tokens, admin token)\n        │\n   ProvisionerService (provisioner.ts — validate → render → measure →\n        │              ceremony → sealed commit → evidence)\n   ┌────┴─────┐\n CvmDriver  Governance     (drivers.ts — the only seams to the world)\n   │           │\n Phala Cloud  Base SCA     real: phala.ts / chain.ts (⚠️ see below)\n FakeCvm     FakeGov       dev/test doubles, same code path + semantics\n```\n\n## Run it\n\n```sh\nnpm install          # .npmrc pins legacy-peer-deps (permissionless/ox peer skew)\nnpm test             # includes verify vs the REAL committed quote\n./test/zero-egress/run-tests.sh   # zero-egress containment proof (needs docker)\nnpm run dev          # everything on the fakes; seeds a RAG-demo-shaped tenant\n                     # console: http://localhost:4100  (token printed at boot)\n                     # POST /dev/approve/:id = the owner SCA's wallet double\n```\n\n## Publish to npm\n\n`./publish.sh` does the whole flow: `npm login` (only if not already\nauthenticated), a clean `npm ci`, `npm pack --dry-run` to show what\nships, then `npm publish`. There is no separate compile step — the\npackage ships TypeScript source and the CLI runs it via `tsx` (a runtime\ndependency); `prepublishOnly` re-runs typecheck + tests as the final\ngate inside `npm publish`. Requires publish rights to the\n`@ember-sovereignty` org. `files` in `package.json` keeps tests and\n`src/test-support/` out of the tarball. Note that a public npm package\nmakes this directory's source publicly readable.\n\nCLI (`bin/ember.mjs`, no build step):\n\n```sh\nexport EMBER_API=http://localhost:4100 EMBER_TOKEN=emv_…\nember init            # scaffold ember.json (the index card)\nember deploy          # secrets read from your shell env, sealed at deploy\nember status | list\nember egress add api.example.com    # a governed ceremony, not a config edit\nember release name@sha256:…         # new vendor release, same ceremony\nember verify          # trustless of this service: quote + Base RPC directly\n```\n\n## Self-custody release governance (#65, P9/#82)\n\n**The pipeline never signs.** Every deployment names the SCA that owns\nits DstackApp at create (`ownerSca` — the vendor org's SCA by default\nwith an `ember login` credential, explicit and required with an emv_\ntoken; an institution's SCA for institution-governed apps). Fail-closed:\nno owner, no deployment — there is no Ember-held default, and the\nGovernance driver contains no signing capability at all.\n\nEvery governed change is two-stage:\n\n1. **Propose** — `ember release`/`egress add` measures the new compose;\n   the deployment goes `pending-approval`; nothing is signed.\n2. **The owner approves** — the owning SCA's holders review the proposal\n   (the console's detail view renders the image/egress/compose diff and\n   the exact hash to sign) and run `addComposeHash` out-of-band from\n   their own wallet.\n3. **Pipeline commits** — the approval watcher polls\n   `allowedComposeHashes(hash)` on Base (`APPROVAL_POLL_MS`, default 30s)\n   and, the moment it flips true, upgrades the CVM and recaptures\n   evidence. **The chain is the approval bus** — no trust in our\n   database, and no code path commits without it.\n\nA second watcher reconciles the record with the platform (#136): every\n`RECONCILE_POLL_MS` (default 60s) it reads the CVM state of settled\ndeployments and flips `running`↔`stopped` when the platform disagrees\n(e.g. a billing suspension stopping every CVM on the account), recording\nthe raw report as `platformStatus`/`platformCheckedAt`. `ember status`\nadditionally live-probes each endpoint (HEAD, 5s timeout) and prints\nreachability next to the recorded lifecycle status.\n\n`ember release --wait` polls until the proposal commits; the default is\nasync (\"proposal recorded; awaiting the owner SCA's approval\"). Timeout /\nwithdrawal / rejection semantics are decided (Chris, 2026-07-30; #65) —\nexplicit off-chain withdraw/reject + proposal TTL, `removeComposeHash` as\nthe on-chain revocation of a granted approval, pre-commit `isAllowed`\nre-check, and allowlist pruning to the current hash after commit. Full\nrationale: `plans/SAML-SSO-ACTION-SIGNER-PLAN.md` (\"#65 proposal-lifecycle\nmechanism\"). Not yet implemented.\n\nDev/test doubles for the owner's wallet: `npm run dev` exposes\n`POST /dev/approve/:id`; unit tests call `approveOutOfBand` on the fake;\n`scripts/live-dogfood.ts` holds a funded test-vendor Kernel SCA in the\nharness and signs the real userop — the pipeline under test never signs.\n\n`ember verify` is the product's honest line, executable: it fetches the\nquote (live with `PHALA_CLOUD_API_KEY`, else the stored one), hashes the\nmeasured `app_compose`, and checks `allowedComposeHashes` + `owner()` on\nBase (`BASE_RPC_URL`, default mainnet). The same function ran green\nagainst the real contract `0x4697B0De…F428e` with the committed\n2026-07-28 quote.\n\n## Intake rules (v1)\n\n- **Image**: `name@sha256:<digest>` only; tags rejected. Must be\n  `linux/amd64` and anonymously pullable (ghcr.io / Docker Hub public\n  repos; not an expiring registry like ttl.sh — the pinned digest must\n  stay pullable for the deployment's lifetime).\n- **Egress**: exact hostnames on 443 only — each becomes a docker alias\n  on the vey container + a measured SNI `exact_match`. No wildcards, no\n  suffixes (needs Mode A/two-CVM), no IP literals. Any change = new\n  compose hash = SCA approval = upgrade ceremony.\n  **`egress: []` is first-class**: the render carries no vey at all\n  (nothing to allowlist means no exit exists) — the app stays on the\n  internal-only network with a loopback DNS upstream, so it can initiate\n  no outbound TCP and no outbound DNS while its declared ports keep\n  serving through the ingress relays. The compose states it with a\n  column-0 `# egress-policy: none` marker (what verify/console/trust\n  surfaces read as \"in-measure zero\", distinct from \"couldn't parse\").\n  The field stays required: an absent list is a 400 — only an explicit\n  `[]` declares zero. A custom domain is refused on a zero-egress\n  deployment (the domain-ingress sidecar performs its own ACME/DNS-API\n  egress, which would falsify the claim — fail-closed until the sidecar\n  is contained). Containment proof: `test/zero-egress/run-tests.sh`\n  (docker; also a CI job) — inbound answers, outbound TCP/DNS die, and a\n  positive control shows an uncontained container on the same host CAN\n  get out.\n- **Secrets**: names declared in the spec; values read at deploy and\n  sealed via Phala's encrypted env — never stored, never in the compose\n  (placeholders only, verified in tests against the real quote).\n- **Resources**: optional `spec.resources` (`cpus` ≤ 16, `memoryMb` ≤\n  32768, `diskGb` ≤ 200) sizes the CVM at create — the driver maps it to\n  the smallest TDX instance that fits (default `tdx.medium`, 40 GB).\n  Never part of the measurement; not resizable after create.\n\n## What's real vs pending\n\n- Orchestration, validation, render, ceremony sequencing, evidence\n  verification: tested here, incl. against the live CVM's committed\n  attestation (`src/verify.test.ts`).\n- **Whole lifecycle proven live (2026-07-28, `scripts/live-dogfood.ts`):**\n  create — the RAG demo image to a running attested CVM in ~2 min, with\n  the DstackApp **owned by the governance SCA from block one** (the\n  factory's `initialOwner` parameter; the gas-paying EOA never holds\n  authority, no ownership transfer exists). Update — a live egress-add\n  ceremony in 74s: new measured hash, SCA `addComposeHash` userop\n  (tx `0xded8c4c3…8794`), CVM upgrade, `ember verify` green with both\n  hosts decoded from the live quote. CVMs stopped after (cost\n  discipline). Since P9 (#82) the signer lives in the dogfood harness\n  (playing the vendor's wallet), not the product; the owner SCA must\n  hold gas before its first userop — the harness funds it explicitly,\n  never implicitly.\n- Read-only console (`public/`): list + detail; the egress panel renders\n  from the **measured** compose when evidence exists, not our records.\n- Store: memory/JSON-file; Postgres rides issue #3. Vendor auth: bearer\n  tokens; passkey→SCA rides issue #2.\n","readmeFilename":"README.md"}