Skip to main content
A registry is a catalog of installable packages. A package can be a button or a drawer. The registry answers: “What packages are available, what kind are they, what immutable versions exist, and what content hash should I install?” Registry installs use three different files:
buttons.json is the project manifest. buttons-lock.json is the repeatable resolution. Installed button.json and drawer.json files stay focused on the local package spec; they do not store registry URLs, bearer keys, or the dependency source of truth.

Manifest

.buttons/buttons.json is user-authored desired state:
Dependency keys must be scoped package names in the form @desk/name. Names are unique per desk across buttons and drawers. The package kind is registry metadata, not part of the name. Version ranges and per-dependency option objects are intentionally not part of the MVP.

Lockfile

.buttons/buttons-lock.json records the exact resolved set:
The lockfile is generated by buttons add, buttons install, and buttons update. It is committed for repeatable installs. It must never contain registry bearer keys or battery values.

button.json

Each installed button still has its own runtime spec:
requires uses the same version policy as the root manifest: "latest" or an exact version. Transitive dependencies are resolved into the lockfile beside root dependencies.

drawer.json

Each installed drawer has its own workflow/package spec:
Installing a drawer writes .buttons/drawers/<name>/drawer.json and installs every button referenced by its steps. Step button names are local names; package resolution uses the drawer’s desk namespace. For example, @your-desk/deploy-pack with step "button": "build" installs @your-desk/build.

Command Flow

Use the registry like a package manager:
  • buttons add @desk/name writes .buttons/buttons.json with "latest" and installs immediately. The package can be a button or drawer.
  • buttons add @desk/name@1 writes an exact pin and installs that immutable version.
  • buttons install takes no package argument. It reads .buttons/buttons.json and honors .buttons/buttons-lock.json for repeatability.
  • buttons status reports available CLI and content updates.
  • buttons update updates the CLI and floating package dependencies only.
buttons install @desk/name is intentionally invalid. Use buttons add @desk/name to add a dependency.

Auth

Hosted-registry requests are bearer-authed (Authorization: Bearer <key>). Read and write use distinct batteries: The registry base URL comes from $BUTTONS_REGISTRY_URL. This public repo does not bake in a real registry host. Bearer keys authorize what an operation may do; the workspace’s agent identity proves which device is acting. Enroll a device once with buttons agent setup before publishing to a hosted registry.

Publish Flow

buttons publish @desk/name publishes a local button or drawer folder to the configured registry:
The on-disk package is found by its bare local name (hello or deploy-pack), while @your-desk/name is the registry identity. Publish detects the package kind from button.json or drawer.json. Published versions are immutable. New buttons and drawers start at version 1; if that version already exists, publish bumps the local package spec to the next number and retries. Publishing a button bundles button.json, code files, and AGENTS.md. Publishing a drawer bundles drawer.json and AGENTS.md. Neither path uploads pressed/ run history or battery values.

Version Example

  1. Publisher runs buttons create hello --code 'echo hello'.
  2. Publisher runs buttons publish @your-desk/hello.
  3. Agent workspace runs buttons add @your-desk/hello.
  4. Agent lockfile resolves @your-desk/hello to version 1.
  5. Publisher changes the source button and publishes again. Publish auto-bumps to version 2 if 1 already exists.
  6. Agent workspace runs buttons status to see the available update.
  7. Agent workspace runs buttons update to move the floating dependency to the new version.
If the agent had run buttons add @your-desk/hello@1, step 7 would leave it pinned at that exact version.

Hardening

A registry is treated as untrusted. Downloads are size-capped, tarball hashes are verified before extraction, file-content hashes are written to the lockfile, and extraction rejects absolute paths, traversal paths, macOS sidecar files, and run history. Before overwriting an installed package, update compares the current local files with the lockfile/install-state hash. Local edits are skipped, not overwritten.

HTTP Contract

A hosted registry implements:
  • GET /v1/meta — optional registry metadata, including minimum CLI version.
  • GET /v1/index — catalog entries { name, kind, version, tags, sha256 }, where kind is button or drawer and sha256 is the tarball hash. Latest is the last entry for a given name.
  • GET /v1/buttons/<@desk/name>/<version>/download — download the gzip tarball; should set X-Content-Sha256.
  • POST /v1/buttons/<@desk/name>/<version> — publish an immutable version with headers Content-Type: application/gzip, X-Content-Sha256, and X-Button-Kind.