Companion to VISION.md. This document is the systems map: how
the binary is wired, how the lifecycle pipeline runs, and where each seam
lives. Schema detail lives in docs/SCHEMA.md. Provider
onboarding is in docs/ADDING-A-PROVIDER.md.
Code comments cite sections as ARCHITECTURE.md §N — section numbers are
stable.
Nothing here is speculative. §5 is explicitly phased/TBD; everything else is decided and reflected in the workspace.
flowchart TB
CLI["stackless CLI / Client"]
Engine["Engine<br/>plan · execute · checkpoint"]
Store[("SQLite state store")]
Sub["dyn Substrate"]
Int["stackless-integrations"]
Stripe["stackless-stripe-projects"]
Daemon["stackless-daemon"]
Git["stackless-git"]
CLI --> Engine
Engine --> Store
Engine --> Sub
Sub --> Int
Int --> Stripe
Sub -->|local| Daemon
Sub --> Git
Daemon -->|reaper spawns down| CLI
Layers. The CLI/Client opens the store, resolves secrets, builds a
substrate from the binary registry, and drives the engine. The engine is
substrate-agnostic: it plans steps, checkpoints, and reconciles via
observe. Substrates own materialization, hooks, start, and health.
Integrations are not substrates — substrates call
stackless-integrations for provision/observe/destroy, which drives the
Stripe Projects catalog. Only the local substrate needs the daemon
(proxy, supervision, reaper host).
flowchart TD
A["Client::up"] --> B["secrets + validate_all"]
B --> C["substrates::build"]
C --> D["Engine::up"]
D --> E["claim_lock + renew_lease"]
E --> F["StackDef::plan"]
F --> G{"for each Step"}
G -->|checkpoint Present| H["skip"]
G -->|absent| I["Substrate::execute"]
I --> J["record_checkpoint"]
H --> G
J --> G
G -->|done| K["renew_lease"]
Step kinds, in plan order per dependency-graph topo node
(engine/plan.rs):
StepKind |
Prefix | Role |
|---|---|---|
ProvisionIntegration |
integration: |
catalog resource for each integration |
Materialize |
materialize: |
source checkout or source-ref journal |
Setup |
setup: |
once-ish toolchain/deps (if declared) |
Prepare |
prepare: |
every up, deps ready → before start |
Start |
start: |
process / cloud deploy |
HealthGate |
health: |
public-origin health |
There is no separate resume verb: up on an existing name resumes.
Recorded steps whose observe returns Present are skipped.
down runs the journal in reverse (destroy + observe survivors) and
tombstones the instance. verify is outside the plan — see §7.
v0 ships the lifecycle layer in the system map above. Destination trust boundary work (§5) stays additive.
- Rust core, pluggable provisioning. One workspace, one
stacklessbinary. The core owns identity, state, lifecycle, wiring, verification, leases, and teardown. Substrate backends may drive external CLIs where those earn their keep — the Stripe Projects CLI is the internal catalog driver for cloud provisioning and spend tracking. Each cloud substrate also talks to the provider's REST API for what Stripe Projects cannot express: interpolated env vars, deploy triggers, deploy polling, health waits, and teardown verification. Operators never declare "use Stripe Projects" instackless.toml— stackless always does when a catalog resource is needed. - The trust boundary is sequenced, not shipped, in v0. Default-deny egress and secret blinding remain the destination (VISION.md), but v0 keeps the seam: every resource an instance owns is named and labeled with the instance name, so wrapping an instance in its own network later is an addition, not a redesign.
- v0 secrets posture: secrets flow as env vars, sourced from the Stripe Projects vault / pulled env files, visible to the operator, protected by being test-scoped credentials.
The definition subsystem turns stackless.toml into an ordered step plan.
Full field reference: docs/SCHEMA.md.
flowchart LR
Toml["stackless.toml"] --> Parse["def model"]
Parse --> Interp["interp refs"]
Interp --> Graph["DependencyGraph"]
Graph --> Plan["Step plan"]
Decided:
- Format: TOML, in a
stackless.toml. The schema is deliberately shallow; serde + comments fit Rust-native culture. - A service is substrate-independent identity + wiring + health; how
a substrate runs it is nested per substrate (
[services.api.local],[services.api.render],[services.web.vercel], …). - Code sources are git references (
repo+refper service).upmaterializes each service's source into instance-owned space. A per-invocation--sourcepin uses an existing checkout — local-only; cloud substrates deploy committed refs, so--sourcewith--on render|vercel|…fails validation. With--source … --dirty, each pin's working tree is snapshotted as a content-addressed synthetic commit in instance-owned space. Bare--sourceuses the checkout in place (single active instance per path). On Vercel,source.repomust be a public GitHub HTTPS remote. - Wiring is interpolation; the dependency graph is derived from it.
Env values reference a namespace evaluated per instance per substrate.
If service A's env references an integration, that is an ordering
edge.
${services.X.origin}is recorded as wiring but is not a topo edge — origins are derivable from the instance name alone on local/Render (Vercel uses the deployment URL afterstart), so mutual CORS references (api ↔ web) are not cycles. No separatedepends_on. - Two optional per-service lifecycle hooks.
setupruns after materialization (toolchain, deps).prepareruns on everyup, after dependencies are ready and before the service starts (migrations, seed). On cloud substrates,prepareexecutes on the operator's machine from materialized source with the instance env exported. Both hooks are contractually safe to re-run. - Health gates
up;verifyproves. Every service declares ahealthcheck;uprefuses success until all pass. The stack declares oneverifycommand (named tiers can be added later) run by theverifyverb with origins/env exported. - Hosted integrations separate logical name from catalog provider.
[integrations.<name>]is the interpolation slot (${integrations.<name>.<output>});providernames the catalog adapter. Each provider declares managed (global config only) or host-bound (tied to stack hosts). Provider config is validated bystackless-integrations;${integrations.*}references are ordering edges.
| Reference | Resolves to | Notes |
|---|---|---|
${stack.name} |
the stack's declared name | useful for hosted integration names |
${instance.name} |
the instance's name | the one identity everything derives from |
${services.X.origin} |
substrate-appropriate origin | local: http://x.{instance}.localhost:<port>; Render: https://{stack}-{instance}-x.onrender.com; Vercel: deployment URL after start. Mutual service refs are not topo edges |
${secrets.KEY} |
resolved secret value | secrets.required injects same-named vars |
${integrations.X.<output>} |
provider output | from integration checkpoint payloads |
$PORT |
OS-allocated port | injected into local run only — not interpolation |
Resolution rules: substrate env overlays the common env; references
to anything undeclared fail validation at parse time, not at up time.
Deliberately absent from the schema: lease duration (--lease flag with
substrate defaults), dirty-worktree override (per-invocation, local-only,
recorded in the manifest), image: runners and third-party egress
(reserved seams), and any depends_on key.
Secrets resolution. When [stack.projects.stripe].project is
recorded, stackless pulls the Stripe Projects vault as the base; a
gitignored .stackless.env next to stackless.toml overlays it (file
wins). Local-only stacks without a Stripe anchor stay env-file-only. A
required key that resolves from neither fails before anything
provisions. stackless doctor runs stripe projects --preflight to
surface auth/ToS/provider-link blockers before up.
The control plane: verbs, identity, locks, and the durable journal.
flowchart LR
subgraph verbs [CLI verbs]
up[up]
down[down]
verify[verify]
status[status / list]
logs[logs]
end
Engine[Engine]
Store[(Store)]
Sub[Substrate]
VerifyCmd["stack.verify command"]
up --> Engine
down --> Engine
Engine --> Store
Engine --> Sub
verify --> VerifyCmd
verify --> Store
status --> Store
logs --> Sub
Decided:
- Verbs.
up [--name],down,verify,status,list,logs(local: daemon-captured file output; cloud: recent API window viarender_api/vercel_api/fly_events/netlify_api/railway_api/cloudflare_api/wordpress_api/laravel_cloud_api/gitlab_api).upon an existing instance resumes.--nameis optional at creation ({stack.name}-{uuid}when omitted). The substrate is chosen at creation only (--on local|render|vercel|fly|netlify|railway|cloudflare|wordpress| laravel-cloud|gitlab), becomes part of instance identity, and is never asked again. Names are unique across substrates in the state store.up --on <s>fails if any service lacks that substrate's config. All commands are non-interactive, support--json, and use agent-branchable exit codes. Anything that spends money requires--confirm-paid. - One operation at a time per instance. Mutating verbs take a per-instance operation lock (PID + process start time); a second invocation fails fast. The reaper respects the lock (§6).
- Parallel
upacross different names is supported. Cross-process file locks serialize shared writers: Stripe Projects CLI invocations keyed bydefinition_dir, and bare git cache clone/fetch keyed by source URL. Parallel agents should use one git worktree each; bare--sourceon multiple active instances is refused — use--dirtyor distinct checkouts. - Errors are an agent-facing contract. Every error carries what
failed, why, and how to proceed. In
--jsonmode:schema_version, stablecode, optionalstep/instance,context,remediation. Agents branch on codes, never prose. stdout carries final envelopes; stderr carries NDJSONupprogress in--jsonmode. - Identity. DNS-safe instance name, persisted in the manifest. Nothing is re-derived from the working directory at runtime.
- State: a SQL state store. Instance records, leases, operation
locks, and the per-step checkpoint journal live in SQLite under the
per-user XDG state dir. Local engine:
rusqlite(bundled SQLite, WAL) — thetursocrate's exclusive per-process file lock cannot serve concurrent CLI + daemon. Opt-in fleet plane:libsqlremote mode for shared CAS leases/locks across operator machines. - Teardown leaves a tombstone. After verified
down/reap, rows flip to tombstone and logs survive a GC window; billable resources are gone. - Resume reconciles against observation. On resume, each recorded
step is re-checked via
substrate.observe— the manifest says where to look; the substrate says what's true.
flowchart TB
Root["$XDG_STATE_HOME/stackless"]
Root --> DB["state.db"]
Root --> Sock["daemon.sock"]
Root --> Sources["sources/instance/service/"]
Root --> Logs["logs/instance/"]
Root --> Cache["cache/git/"]
DB --> T1["instances"]
DB --> T2["leases"]
DB --> T3["op_locks"]
DB --> T4["checkpoints"]
DB --> T5["reap_attempts"]
Definition-dir sidecars (not in XDG): .stackless.env, Stripe
.projects/, pulled .env / .env.<instance>.
App services run as host processes from commands in the definition.
Toolchain provisioning is the repo's business (setup hooks). Everything
meets at localhost ports allocated per instance; the built-in reverse
proxy plays the portless role so origins derive from the instance name
alone.
flowchart TB
Local[LocalSubstrate]
Daemon[Daemon unix socket]
Proxy[Host-header proxy]
Sup[PID supervision]
Reaper[Lease reaper]
Local -->|RouteSet / Supervise| Daemon
Daemon --> Proxy
Daemon --> Sup
Daemon --> Reaper
Reaper -->|"stackless down --json"| DownPath[verified down path]
- Schema separates what a service is from how a substrate runs
it. A container
image:runner can be added later without breaking definitions written today. - Teardown is verified: SIGTERM then SIGKILL on the process group,
confirmed dead by PID + start time; proxy route withdrawn.
downexits non-zero listing survivors if anything remains. Then tombstone (§2).
Rationale for host processes over containers-only: the container-build penalty on macOS is paid on every agent cycle, while the fidelity containers would buy is exactly what cloud substrates exist to prove.
One resident component per user hosts everything that must outlive a CLI invocation: reverse proxy, process bookkeeping, and the lease reaper. Cloud substrates need none of this for process keep-alive — but the same reaper enforces all substrates' leases from the operator machine.
- Same binary.
stacklessrunning internaldaemon run. The Rust SDK'sClient::system()resolves that CLI viaSTACKLESS_BIN/PATH. - Spin-up on demand. Commands connect to a unix socket in the state dir; if nothing answers, the CLI spawns the daemon under a lock and waits. No setup step.
- Boot persistence. On first start the daemon registers as a launchd
user agent (macOS) / systemd user unit (Linux). If registration is
refused, stackless degrades loudly: leases are enforced only while the
daemon happens to be running, and
statussays so. - Instance processes are not the daemon's children. Spawned in their own sessions; supervised by recorded PID + start time (PID-reuse-safe); stdout/stderr to size-capped, rotated log files.
- Upgrade = restart + re-adopt. For dist installs, CLI self-update (axoupdater + re-exec) precedes the existing drain/re-adopt handshake. Socket handshake carries version; newer CLI drains older daemon. Starting daemon reconciles manifests against observed reality — re-adopting live processes, noting dead ones.
- v0 supervision: observe, don't restart. A crashed service marks the
instance unhealthy; agents re-run
upto recover.
- HTTP-only proxy in v0, one fixed unprivileged port (configurable
globally, never per instance): origins stay
http://{service}.{instance}.localhost:<port>. TLS mode is a later opt-in. - One service may declare
root_originand also claimhttp://{instance}.localhost:<port>. - Ports are OS-allocated at
up(bind:0) and injected as$PORT. - Routes on the Host header from a table the daemon updates as instances come and go.
All cloud substrates share one pattern: Stripe Projects provisions and
tracks spend; the provider REST API operates (env, deploy, poll,
health, teardown verify). Shared helpers live in stackless-cloud
(prepare, health, credentials, checkpoints). Registration is one row in
crates/stackless/src/substrates.rs.
sequenceDiagram
participant Eng as Engine
participant Sub as CloudSubstrate
participant SP as StripeProjects
participant API as ProviderREST
Eng->>Sub: execute Start
Sub->>SP: catalog add / env membership
Sub->>API: push env + deploy + poll
Sub->>API: health on public origin
Note over Eng,API: down reverses: destroy via Stripe/API then observe survivors
Shared rules:
- One long-lived Stripe project per stack holds hosted integrations
and cloud instances as named environments. Project id is recorded at
[stack.projects.stripe].projectafter first creation. - Per-instance resource names:
{stack}-{instance}-{service}, DNS-safe by construction (§2). - Sequencing: provision integrations →
prepareon the operator machine → push env → deploy → health gate. - Every step checkpoints before proceeding; interrupted runs resume rather than duplicate.
- Teardown is verified, dependents-first; exit non-zero if anything
that bills or holds state remains. Spend is printed after cloud
up/down. - Stripe Projects is the authoritative inventory for recovery when
the state store and reality drift (
stripe projects pull,services list --json). - Paid tiers are never auto-confirmed —
--confirm-paidper invocation, backed by hard per-provider spend caps on the stack project. - No root-origin alias on cloud; each service keeps its own public URL. Setup is typically skipped (cloud builds own the toolchain); prepare still runs on the operator machine from a shallow clone.
- Plugin surface pinned via committed snapshots in
crates/stackless-stripe-projects/tests/fixtures/(nightly watcher opens upgrade PRs).
- Catalog:
render/web-serviceorrender/static-sitefrom[services.X.render]. - Stripe provisions; Render REST fills env, SPA rewrite, deploy trigger, deploy polling (Rust release builds can take 30+ minutes on small tiers), health wait. API key from env or scoped key file.
stackless logsfetches a recent per-service window via the Render API (source: "render_api"; no streaming).
- Catalog:
vercel/project({"name": …}); optional stack-levelvercel/prowhen[stack.vercel].plan = "pro". - After Stripe links the project: push env, git deployment from pinned
ref+[services.X.vercel]build settings, poll untilREADY, health-gate on deployment URL. Token:VERCEL_TOKENor.vercel-token. stackless logsfetches deployment build events via the Vercel API (source: "vercel_api"; recent window, no streaming).
- Catalog:
flyio/app(usage-billed → always--confirm-paid). App name = resource name (Fly naming rules). - Two deploy paths:
[services.X.fly].image(Machines API fast path) or source-build viaflyctl deploy --remote-onlywhenimageis omitted (optionaldockerfile; requiresfly/flyctlon PATH). Smokes:smoke-fly(image) andsmoke-fly-build(source-build). - Stripe provisions; allocates IPs, creates/updates the machine, health-
gates on
https://{app}.fly.dev. Deploy token is Stripe-managed and ephemeral —observe/downkey off Stripe registration. Hand-written reqwest Machines client + flyctl for remote builds. stackless logsfetches machine events via the Fly Machines API (source: "fly_events"; recent window, no streaming).
- Catalog:
netlify/project(free — no--confirm-paid). - Two deploy paths: file-digest static upload (fast path when
buildis absent) or build settings (build/install/root/publish) with zip-upload to the build API (deploy = "build") or git-linkedcreateSiteBuild(deploy = "git"). Smokes:smoke-netlify(static) andsmoke-netlify-build(build API). - Stripe provisions; Netlify REST deploys and polls to
ready, health- gates onssl_url. Token ephemeral —observe/downkey off Stripe. Hand-written client. stackless logsfetches recent deploy log lines via the Netlify API (source: "netlify_api"; recent window, no streaming).
- Catalog:
railway/hostingfrom[services.X.railway]. - Two deploy paths:
[services.X.railway].image(GraphQL image deploy; optionalcmd) or GitHub HTTPSsource.repo+ pinnedrefwhenimageis omitted. - Stripe provisions; Railway GraphQL creates project/service, deploys,
attaches a public domain, health-gates on the live origin. Deploy token
from Stripe instance env (
RAILWAY_TOKEN/RAILWAY_API_TOKEN) or.railway-token;observe/downkey off Stripe registration. stackless logsfetches a recent deployment/build window via Railway GraphQL (source: "railway_api"; no streaming).
- Catalog:
cloudflare/workersper deployable service. Distinct from Cloudflare catalog integrations (cloudflare-r2,cloudflare-kv, …) which run under--on localand are covered bysmoke-cloudflare-integrations; substrate coverage issmoke-cloudflare-workers. - Deploy: clone the pinned ref; upload
worker.js/worker.mjsunder[services.X.cloudflare].rootwhen present, otherwise embedindex.htmlin a generated module Worker. Scripts API upload → health-gate on*.workers.dev. - Stripe provisions; deploy uses
CLOUDFLARE_API_TOKEN(Stripe env, secrets, or.cloudflare-api-token).observe/downkey off Stripe. stackless logsfetches recent Workers script/deployment events (source: "cloudflare_api"; no streaming).
- Catalog:
wordpress.com/sitefrom[services.X.wordpress](optionalplan,root). - Stripe provisions; WordPress.com REST publishes static HTML from the
pinned ref under
root, sets the front page when allowed, health- gates onSITE_URL. Token:WORDPRESS_COM_ACCESS_TOKEN/WORDPRESS_ACCESS_TOKENor.wordpress-com-token.observe/downkey off Stripe. Domain purchase (wordpress.com/domain) is excluded. stackless logsfetches a recent site activity window (source: "wordpress_api"; no streaming).
- Catalog:
laravel_cloud/applicationfrom[services.X.laravel-cloud](repository, region, …). - Stripe provisions
app_id; Laravel Cloud JSON:API resolves the environment, POST/environments/{id}/deployments, polls todeployment.succeeded, health-gates on the app origin. Token:LARAVEL_CLOUD_API_TOKENor.laravel-cloud-token.observe/downkey off Stripe registration. stackless logsfetches recent deployment log lines (source: "laravel_cloud_api"; no streaming).
- Catalog:
gitlab/projectfrom[services.X.gitlab](optionalvisibility,root). - Stripe provisions; GitLab REST commits static files under
public/from[services.X.gitlab].root, installs a Pages CI job (.gitlab-ci.yml), polls thepagesjob (~15m budget), health-gates on the public Pages URL. Token:GITLAB_TOKEN/GITLAB_ACCESS_TOKENor.gitlab-token.observe/downkey off Stripe registration. stackless logsfetches recent Pages/CI job log lines (source: "gitlab_api"; no streaming).
Recorded from design discussion, to be developed when sequenced:
- Two separable jobs: (1) default-deny egress — an SNI-passthrough gateway on a per-instance network, no MITM, no app changes; (2) secret blinding — token→real-value swap at egress.
- Two-class secret model: instance-minted secrets (DB passwords, per-instance keys) are protected by leasing alone — they die with the instance and are useless outside it; blinding them buys nothing. Third-party durable secrets are the dangerous class and the only candidates for blinding.
The reaper lives in the local daemon (§3) and enforces leases for
instances on all substrates through the same teardown path down
uses. Known gap: if the operator's machine is off/asleep past a cloud
lease's expiry, the instance outlives its lease until wake (the daemon
reaps overdue leases immediately on start/wake). A substrate-side
backstop is a candidate for later.
flowchart TD
Birth["up: lease from birth"] --> Renew["renew at mutating verb start"]
Renew --> Success["renew again on successful up / verify"]
Success --> Tick["reaper tick ~60s"]
Tick --> Locked{"op lock held?"}
Locked -->|yes| Skip[skip]
Locked -->|no + expired| Down["spawn stackless down"]
Down --> Tomb["tombstone + GC window"]
Skip --> Tick
Lease semantics:
- Every instance carries a lease from birth —
--lease <duration>atup, with per-substrate defaults (local: 24h; cloud: typically 8h). - The lease renews to its full duration at the start of every mutating
verb, and again on a successful
uporverify. Traffic does not renew. No separate renew verb in v0. Consent at creation covers renewals; spend caps (§4) bound total exposure. - The reaper never reaps an instance holding its operation lock. A
failed reap retries with backoff and surfaces in
list/status. listshows remaining lease for every instance.
- Per-service health checks run through the instance's public
origin — locally the proxy; on cloud the provider URL — never the
raw port, so routing is part of what "healthy" proves. Shape:
health = { path, status = 200, contains = "..." }with a retry budget defaulting per substrate (seconds locally; minutes against a cold cloud deploy). upreports staged truth: provisioned → configured → prepared → started → healthy, each stage gated on the previous.statusshows the stage an instance actually reached, per service.verifyruns the stack's verify command with env built by the same interpolation mechanism services use ([stack.verify]hasrunandenvwith${...}references). It renews the lease on the way in and again on success — keepalive plus proof. App-level fixtures are the stack's own business.
flowchart LR
Up["up HealthGate"] --> Origin["public origin"]
Verify["verify verb"] --> Interp["build namespace"]
Interp --> Cmd["stack.verify.run"]
Cmd --> Lease["renew lease on success"]
One Cargo workspace; seams mirror the load-bearing boundaries so each substrate compiles and tests in isolation.
flowchart TB
Bin["stackless bin<br/>CLI · Client · substrates.rs"]
Core["stackless-core<br/>def · store · Engine · Substrate trait"]
Daemon["stackless-daemon"]
Local["stackless-local"]
CloudHelp["stackless-cloud"]
Git["stackless-git"]
Int["stackless-integrations"]
Sdk["stackless-provider-sdk"]
Stripe["stackless-stripe-projects"]
CloudSubs["render · vercel · fly · netlify · …"]
Clients["render-client · vercel-client"]
Bin --> Core
Bin --> Daemon
Bin --> Local
Bin --> CloudSubs
Bin --> Int
Local --> Core
Local --> Daemon
Local --> Git
CloudSubs --> Core
CloudSubs --> CloudHelp
CloudSubs --> Git
CloudSubs --> Int
CloudSubs --> Clients
Int --> Sdk
Int --> Stripe
CloudHelp --> Core
Daemon --> Core
Ground rule: stackless-core never names a substrate. The binary
registers hosting providers in substrates.rs (one row + crate).
Integrations register via Hostable / ProviderOps in
stackless-integrations. See docs/ADDING-A-PROVIDER.md.
Workspace conventions:
- Parsing/CLI foundations are pinned:
toml+ serde forstackless.toml;clap(derive) for the CLI. - Errors are
thiserrorenums end-to-end — noanyhow. Every error must carry its stable code, step/instance context, and remediation to the agent-facing envelope (§2). A new error variant is only complete when its remediation says what the operator should actually do.
| Crate | Role |
|---|---|
stackless-core |
Definition model (serde, validation, interpolation, derived graph), SQL state store, lifecycle engine (plan / execute / checkpoint / observe). Defines the Substrate trait. |
stackless-local |
Local Substrate: process spawn/adoption, port allocation; materialization via stackless-git. |
stackless-git |
Pure-Rust git (grit-lib): bare cache + alternates checkout for local; shallow clone_checkout for cloud prepare; snapshot_worktree for --dirty. |
stackless-daemon |
Unix-socket RPC, process bookkeeping, reaper tick, Host-header reverse proxy. |
stackless-cloud |
Shared cloud scaffolding: prepare hooks, health poll, credentials, checkpoint helpers. |
stackless-stripe-projects |
Neutral Stripe Projects CLI driver: project anchor, environments, catalog add/remove, env materialization, spend caps. |
stackless-integrations |
Hosted integration routing + provider adapters; substrates call provision/observe/destroy. |
stackless-provider-sdk |
Extension traits: Hostable, ProviderOps, CatalogResource. |
stackless-render / -vercel / -fly / -netlify / -railway / -cloudflare / -wordpress / -laravel-cloud / -gitlab |
Cloud Substrate impls. |
render-client / vercel-client |
Generated REST clients from vendored OpenAPI (specs/regen-clients.sh); opt out of workspace lints. |
stackless-idl / stackless-bindgen |
Language-neutral stack IDL + checked-in bindings helper. |
stackless |
Clap CLI, sync Client, substrate registry, daemon spawner, human/--json output. |
xtask |
Provider onboarding: catalog, discover, new-integration. |
The engine is shared by up, resume, daemon adoption, and the reaper —
they are the same machinery, not parallel lifecycle implementations.