Skip to main content
@repo/clone-contract is the internal contract the CLI and the (planned) control plane are built against. It defines what a clone is and how a provider drives one, with no runtime or Docker specifics — that’s what lets local and cloud share one command surface. This page documents that contract for reference, so you understand how clones behave when you point an agent at one.

The CloneProvider interface

The seam. Every method maps to a CLI verb, and implementations must throw a named error, never a bare Error.
Two implementations are envisioned: LocalDockerProvider (today — runs in the CLI, shells to Docker) and CloudProvider (planned — a thin client to the control-plane API). Same interface, different engine.

Core types

CloneSpec — input to create

What the caller wants; the provider fills in the allocated fields.

CloneRecord / CloneInstance

CloneRecord is the JSON-serializable row persisted to ~/.asymmetric/registry.json: id, template, mode, composeProject, database, redisPrefix, ports, state, seed, expose, image / imageDigest, tokens, and createdAt / updatedAt / syncedAt. A clone does not carry its environment — membership lives on the Environment as the single source of truth. CloneInstance is a CloneRecord plus resolved endpoints (api, web) — what create returns. Three fields are optional and additive (older records and templates without them simply omit them):
  • image / imageDigest — the published image ref the clone was spun from and the content digest (<repo>@sha256:…) resolved at spin time. Pinning the digest keeps reset byte-identical even as :latest moves; hone env upgrade repins to the current image. imageDigest is null for local builds with no registry digest.
  • tokens — programmatic-access credentials minted by the clone’s default app at provision time (appId, workspaceId, botUserId, adminUserId, plus a Slack-shaped botToken / userToken). The clone DB stores only hashes, so these are persisted here (in the 0600 registry) for hone env tokens <id> to reprint. They’re redacted from status / ls output and shown by spin / tokens.

CloneState

gone is terminal and never persisted — it lets status report a clone that vanished outside the CLI, which doctor reconciles.

SeedSpec / SeedResult

SeedResult reports written, rejected, a bySource breakdown, and warnings[]. AI partial failures are reported here, not thrown — a few bad records don’t fail the seed.

CloneStatus / ServiceStatus

The live health view (distinct from the persisted CloneState): per-service runtime state (running / exited / unhealthy / missing), port, and healthy, rolled up into a clone-level healthy boolean.

Environments

Environment is the top-level composition primitive: name, members (clone ids, the source of truth for membership), spec (path to the environment.yaml, or null for ad-hoc), state, and timestamps. EnvironmentState is running / partial / stopped / failed. EnvironmentResult (per-member outcome) is reserved for the deferred multi-clone engine (e.g. an env reset loop). See Environments.

RegistryFile

The on-disk shape of ~/.asymmetric/registry.json: a version, an array of clones (CloneRecord[]), and an array of environments (Environment[]). REGISTRY_VERSION is 2; the CLI migrates older files on read (v1 → v2 renamed groups to environments and rebuilt environments from legacy per-clone group tags, so upgrades lose no membership).

FidelityScore

Surfaced by verify and produced by the /verify-api workflow: score (0–1 coverage), operations (ours / theirs / matched), checkedAt, and source. Cached under ~/.asymmetric/fidelity/<template>.json, and never hard-gates spin.

The TemplateManifest

How a template declares its services, modes, database/migrations, Redis prefix, and seed sources — a clone.manifest.json at the template’s app root. Walked through in Templates. This reference is provided so you understand how clones are structured; templates themselves are built and maintained by Hone. Each service declares either a build context (built locally) or a published image ref (pulled, with no source present) — image is the distribution default, e.g. ghcr.io/asymmetric-ai/asymmetric-slack-backend:latest. A service marked optional: true is only run in the modes that name it. Six templates ship today, all on the same manifest shape: Slack is the only one with a full mode today — it ships a build-based frontend service alongside its image backend. The other five are backend-only (api) services pulled as images.

Design rules

Providers run client-side

LocalDockerProvider shells to Docker from the CLI. CloudProvider is a thin client. The interface is the only shared surface.

Throw named errors

Never a bare Error. The CLI switches on the error code to render a clear message. if (isAsymmetricError(e)) ...

Records are JSON

Everything in CloneRecord is JSON-serializable — it lands verbatim in the registry file.

One label

Docker resources carry the asym.clone label so they’re discoverable and reconcilable.