# Program OS API

Build, deploy, upgrade, bind, and inspect Solana programs from GitHub pushes or on-chain imports — authority stays on chain, deployments stay approval-controlled. Explore every endpoint, run them in-page, or point an AI agent at the MCP surface.

You are helping a user call this API. This document is the complete
contract: every endpoint, its parameters and request body, the auth, and
the MCP surface. Generate runnable `curl`/TypeScript/Python on demand
using the values below; replace the placeholder key with the user's real
`k256_live_` key.

- Base URL: `https://api.k256.xyz`
- Data plane: `https://api.k256.xyz/v1/program-os`
- OpenAPI: `https://api.k256.xyz/program-os/api/spec.json`
- MCP (for agents): `https://api.k256.xyz/program-os/mcp` (server name `k256-gateway`) — the same operations as 1:1 tools.
- Auth: every request needs `Authorization: Bearer <YOUR_API_KEY>`. Never put the key in a URL query string.

Program OS controls Solana program deployment lifecycle: import/bind a program, dispatch managed CI builds, stream an ELF into a buffer, run Upgrade (single-sig or multisig via a Wallet OS account), manage IDLs, and bind Address Lookup Tables. On-chain authority is never claimed by the API — every mutating on-chain step returns a prepared transaction the operator signs.

## Endpoints

### `GET /v1/program-os/programs/import-preview` — Preview the on-chain envelope for a Solana program
- Action: `read` · MCP tool: `program-os.preview_import`
- Read-only: decodes the BPF Loader Upgradeable Program + ProgramData accounts and returns the deployed slot + current upgrade authority. Writes nothing.
- Query param `address` (string, required)
- Query param `cluster` (string)

### `GET /v1/program-os/projects` — List projects visible to the workspace
- Action: `read` · MCP tool: `program-os.list_projects`

### `POST /v1/program-os/projects` — Connect a GitHub repo + on-chain program to a managed-build project
- Action: `write` · MCP tool: `program-os.connect_project`
- Binds an installed GitHub repo (installation_id + <owner>/<repo>) and an on-chain program id to a project so pushes dispatch managed CI builds. Org-scoped; re-connecting the same repo updates it in place.
- Request body (JSON, required):
  - `installation_id` — string
  - `repo_full_name` — string (required)
  - `program_id` — string (required)
  - `default_branch` — string
  - `crate_path` — string
  - `name` — string
  - `already_deployed` — boolean
  - `control_mode` — "observed" | "external_authority" | "wallet_os_managed"
  - `wallet_os_account` — any
  - `clusters` — string[]
  - `cluster` — string

### `POST /v1/program-os/projects/from-template` — Create a project from a curated k256 template
- Action: `write` · MCP tool: `program-os.create_from_template`
- Generates a fresh repo in the user's chosen GitHub account from one of our curated template repositories (Anchor/native/Pinocchio hello-world), stamps the program id into the source where the framework needs it, connects the project + program, and starts the first build. Non-custodial: the program keypair is generated client-side and only its pubkey reaches us.
- Request body (JSON, required):
  - `template_id` — string (required)
  - `target_owner` — string (required)
  - `installation_id` — string (required)
  - `repo_name` — string (required)
  - `private` — boolean
  - `program_id` — string (required)
  - `secret_key` — integer[]
  - `program_name` — string
  - `default_branch` — string
  - `clusters` — string[]
  - `cluster` — string

### `DELETE /v1/program-os/projects/{id}` — Delete a project and all its Program OS metadata
- Action: `write` · MCP tool: `program-os.delete_project`
- Deletes the project, every program it owns (all clusters), and their builds/IDLs/deployment candidates/environments/env vars. Does NOT touch the GitHub repo or App installation (your source) or on-chain programs (rent + authority persist). To stop GitHub access, uninstall the App from the repo in GitHub settings.
- Path param `id` (string, required)

### `GET /v1/program-os/projects/{id}/activity` — Project-scoped audit/activity feed
- Action: `read` · MCP tool: `program-os.project_activity`
- The org-wide activity feed filtered to one project — its connects, builds, deploys, upgrades, IDL publishes, and authority changes. Same rows as /program-os/activity, scoped to the project's resource ids. `before` is a unix-ms cursor.
- Path param `id` (string, required)
- Query param `before` (string)
- Query param `limit` (integer)

### `GET /v1/program-os/programs` — List programs visible to the workspace
- Action: `read` · MCP tool: `program-os.list_programs`
- Optional ?wallet_os_account filters to a single account. Merges manifest-managed rows with on-chain kernel_bind bindings.
- Query param `wallet_os_account` (string)
- Query param `cluster` (string)
- Query param `limit` (integer)
- Query param `cursor` (string)

### `GET /v1/program-os/programs/{id}` — Program detail with live chain reconciliation
- Action: `read` · MCP tool: `program-os.get_program`
- Reads the binding row, re-fetches Program + ProgramData, reconciles drift in place, and returns { program, chain, capabilities, drift }.
- Path param `id` (string, required)

### `PATCH /v1/program-os/programs/{id}` — Edit a program binding (name, control mode, authority, branch)
- Action: `write` · MCP tool: `program-os.update_program`
- Updates the editable binding fields: display name, control mode, Wallet OS authority account, and the project default branch. Program id + cluster are identity (re-connect to retarget); crate/build settings use update_build_config.
- Path param `id` (string, required)
- Request body (JSON):
  - `name` — string
  - `control_mode` — "observed" | "external_authority" | "wallet_os_managed"
  - `wallet_os_account` — any
  - `default_branch` — string
  - `program_id` — string

### `DELETE /v1/program-os/programs/{id}` — Delete a whole program (every cluster target)
- Action: `write` · MCP tool: `program-os.delete_program`
- Deletes the logical program — EVERY per-cluster target in its group — and all their Program OS metadata (builds, IDLs, deployment candidates), plus the owning project if it has no other programs. On-chain state is NOT touched. To drop a single cluster, use remove_cluster.
- Path param `id` (string, required)

### `POST /v1/program-os/programs/{id}/keypair` — Seal (or regenerate) the one-time-use program keypair
- Action: `write` · MCP tool: `program-os.set_program_keypair`
- Seals the program account keypair — the 'deploy ticket' that signs ONLY the first-time create (never upgrades) — at rest, so the deploy co-signs server-side and the operator never uploads a .json. If the supplied key's address differs from the program's current address, REGENERATES: repoints every not-yet-deployed cluster target in the group to the new address, then seals. Refused once the address is live on chain.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `secret_key` — integer[] (required)
  - `source` — "managed" | "imported"

### `GET /v1/program-os/programs/{id}/keypair-status` — Whether a sealed program keypair is held for this address
- Action: `read` · MCP tool: `program-os.program_keypair_status`
- Reports if the gateway holds a sealed keypair for the program's address (and whether it's already live). Drives the deploy wizard's up-front availability check — block BEFORE any transaction, never mid-deploy.
- Path param `id` (string, required)

### `POST /v1/program-os/programs/{id}/clusters` — Add a cluster target to a program
- Action: `write` · MCP tool: `program-os.add_cluster`
- Adds another cluster to an existing program. By default it reuses the SAME keypair (the group's canonical address) on the new cluster; pass program_id to point this cluster at a DIFFERENT address (an existing program there). The new target shares the program group, name, and build config but is its own row with its own builds/deployments.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `cluster` — string (required)
  - `program_id` — string
  - `wallet_os_account` — any

### `DELETE /v1/program-os/programs/{id}/cluster` — Remove one cluster target from a program
- Action: `write` · MCP tool: `program-os.remove_cluster`
- Removes a single per-cluster target (the row + its builds/IDLs/deployment candidates). The rest of the program's clusters are untouched. On-chain state is NOT touched — if it was deployed there, the program, its rent, and its authority persist on that chain. Removing the last target removes the program.
- Path param `id` (string, required)

### `POST /v1/program-os/programs/prepare-bind` — Prepare a bind_program transaction
- Action: `write` · MCP tool: `program-os.prepare_bind`
- Builds the real wallet-os-programs bind_program instruction and returns the signable legacy message bytes + required signers.
- Request body (JSON):
  - `authority` — string
  - `wallet_os_account` — string
  - `program_id` — string
  - `programdata_address` — string
  - `current_authority` — string
  - `control_mode` — "observed" | "external_authority" | "wallet_os_managed"
  - `cluster` — string

### `POST /v1/program-os/programs/{id}/instructions/prepare` — Prepare any IDL instruction for signing
- Action: `write` · MCP tool: `program-os.prepare_instruction`
- Builds an instruction declared in the program's IDL (discriminator + Borsh args) into an unsigned legacy message for the caller's own wallet to sign and submit. Non-custodial — the gateway never signs.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `instruction_name` — string (required)
  - `args` — object
  - `accounts` — object
  - `fee_payer` — string (required)

### `PATCH /v1/program-os/programs/{id}/build-config` — Edit where/how a program builds (any time)
- Action: `write` · MCP tool: `program-os.update_build_config`
- Vercel-style editable build settings: change the build root directory (crate_path), framework (auto/anchor/native), explicit build command, toolchain versions, pre-build setup commands, and the output program name — at any point after connecting. The next build uses the latest values.
- Path param `id` (string, required)
- Request body (JSON):
  - `crate_path` — any
  - `build_framework` — "auto" | "anchor" | "native"
  - `build_command` — any
  - `toolchain_solana` — any
  - `toolchain_anchor` — any
  - `toolchain_rust` — any
  - `setup_commands` — string[] | null
  - `output_program_name` — any

### `GET /v1/program-os/programs/{id}/builds` — Cursor-paginated build feed per program
- Action: `read` · MCP tool: `program-os.list_builds`
- Path param `id` (string, required)
- Query param `limit` (integer)
- Query param `cursor` (string)

### `POST /v1/program-os/programs/{id}/builds` — Operator-triggered managed CI build
- Action: `write` · MCP tool: `program-os.create_build`
- Resolves the bound repo's HEAD (or an explicit sha), inserts a build row, and fires a repository_dispatch at the builder repo.
- Path param `id` (string, required)
- Request body (JSON):
  - `sha` — string
  - `branch` — string
  - `build_backend` — "github" | "sandbox"

### `POST /v1/program-os/programs/{id}/deployments/{deployment_id}/verify` — Reproducibility check — rebuild a deployment's source + confirm the bytes
- Action: `write` · MCP tool: `program-os.verify_build`
- Rebuilds a prior deployment's EXACT source commit in the sandbox and confirms the elf_hash reproduces byte-for-byte (the on-chain attestation bytes). Returns the verify build_id + expected hash; the build's `reproduced` flag reports the result. No deploy candidate is minted.
- Path param `id` (string, required)
- Path param `deployment_id` (string, required)

### `GET /v1/program-os/programs/{id}/builds/{build_id}` — Single build detail (+ linked candidate)
- Action: `read` · MCP tool: `program-os.get_build`
- Path param `id` (string, required)
- Path param `build_id` (string, required)

### `GET /v1/program-os/programs/{id}/builds/{build_id}/console` — Live or persisted build console
- Action: `read` · MCP tool: `program-os.get_build_console`
- Streams the build console while a build is running (poll incrementally with ?from=N); once the build ends it serves the persisted snapshot. GitHub-backed builds return the Actions run link instead.
- Path param `id` (string, required)
- Path param `build_id` (string, required)
- Query param `from` (integer)

### `POST /v1/program-os/programs/{id}/builds/{build_id}/cancel` — Cancel an in-flight build
- Action: `write` · MCP tool: `program-os.cancel_build`
- Stops a dispatched/running build. The DB transition to 'cancelled' is authoritative (so a stuck or hung build can always be cleared from the dashboard); for a sandbox build the host is also asked best-effort to kill the VM job. No-op on an already-terminal build.
- Path param `id` (string, required)
- Path param `build_id` (string, required)

### `GET /v1/program-os/programs/{id}/deployments` — Cursor-paginated deployments per program
- Action: `read` · MCP tool: `program-os.list_deployments`
- Path param `id` (string, required)
- Query param `limit` (integer)
- Query param `cursor` (string)

### `GET /v1/program-os/deployments/{id}` — Single deployment candidate detail (+ owning program)
- Action: `read` · MCP tool: `program-os.get_deployment`
- Path param `id` (string, required)

### `DELETE /v1/program-os/deployments/{id}` — Delete a non-live deployment candidate (failed / never-live cleanup)
- Action: `write` · MCP tool: `program-os.delete_deployment`
- Path param `id` (string, required)

### `GET /v1/program-os/github/installations` — List GitHub App installations
- Action: `read` · MCP tool: `program-os.list_github_installations`
- Lists every GitHub App installation known to Program OS, with a live repo count.

### `GET /v1/program-os/github/installations/{installation_id}` — Get a GitHub App installation
- Action: `read` · MCP tool: `program-os.get_github_installation`
- Returns one installation plus its active repositories.
- Path param `installation_id` (string, required)

### `GET /v1/program-os/github/branches` — List a repo's branches
- Action: `read` · MCP tool: `program-os.github_branches`
- Live branch list for a repo (default branch first), so the connect form offers a picker instead of a free-text field.
- Query param `repo` (string, required)
- Query param `installation_id` (string)

### `GET /v1/program-os/github/crate-folders` — List a repo's program-crate folders
- Action: `read` · MCP tool: `program-os.github_crate_folders`
- Directories in the repo that contain a Cargo.toml (program-crate roots), so the connect form can offer a crate-path picker.
- Query param `repo` (string, required)
- Query param `installation_id` (string)
- Query param `ref` (string)

### `GET /v1/program-os/programs/{program_meta_id}/idls` — List the IDLs stored for a program
- Action: `read` · MCP tool: `program-os.list_idls`
- Returns row metadata (NOT the IDL JSON); use the per-IDL fetch for the JSON.
- Path param `program_meta_id` (string, required)
- Query param `limit` (integer)
- Query param `cursor` (string)

### `GET /v1/program-os/programs/{program_meta_id}/idls/onchain-address` — Derive + probe the program's Anchor IDL PDA
- Action: `read` · MCP tool: `program-os.get_idl_onchain_address`
- Path param `program_meta_id` (string, required)

### `GET /v1/program-os/programs/{program_meta_id}/idls/{idl_id}` — Get the full IDL JSON for one row
- Action: `read` · MCP tool: `program-os.get_idl`
- Honours the visibility flag against the caller's org.
- Path param `program_meta_id` (string, required)
- Path param `idl_id` (string, required)

### `PATCH /v1/program-os/programs/{program_meta_id}/idls/{idl_id}` — Update an IDL's visibility / description / current pointer
- Action: `write` · MCP tool: `program-os.update_idl`
- Path param `program_meta_id` (string, required)
- Path param `idl_id` (string, required)
- Request body (JSON):
  - `visibility` — "public" | "org_only" | "private"
  - `description` — string | null
  - `set_current` — boolean

### `DELETE /v1/program-os/programs/{program_meta_id}/idls/{idl_id}` — Delete an IDL row
- Action: `write` · MCP tool: `program-os.delete_idl`
- Path param `program_meta_id` (string, required)
- Path param `idl_id` (string, required)

### `POST /v1/program-os/programs/{program_meta_id}/idls/upload` — Upload an operator-pasted IDL
- Action: `write` · MCP tool: `program-os.upload_idl`
- Path param `program_meta_id` (string, required)
- Request body (JSON, required):
  - `idl_json` — string (required)
  - `visibility` — "public" | "org_only" | "private"
  - `description` — string

### `POST /v1/program-os/programs/{program_meta_id}/idls/fetch-from-chain` — Read + persist the program's on-chain Anchor IDL
- Action: `write` · MCP tool: `program-os.fetch_idl_from_chain`
- Idempotent on hash — re-runs against the same on-chain IDL are a no-op.
- Path param `program_meta_id` (string, required)

### `GET /v1/program-os/lookup-tables` — List address lookup table bindings
- Action: `read` · MCP tool: `program-os.list_lookup_tables`
- Lists every ALT binding for the active workspace across clusters.
- Query param `limit` (integer)
- Query param `cursor` (string)

### `GET /v1/program-os/lookup-tables/by-authority` — Scan ALTs controlled by an authority
- Action: `read` · MCP tool: `program-os.scan_lookup_tables_by_authority`
- Finds every ALT on the cluster where the given pubkey is the on-chain authority.
- Query param `authority` (string, required)
- Query param `cluster` (string)

### `GET /v1/program-os/lookup-tables/import-preview` — Preview an ALT live from RPC
- Action: `read` · MCP tool: `program-os.preview_lookup_table_import`
- Reads an ALT account from chain without persisting a binding.
- Query param `address` (string, required)
- Query param `cluster` (string)

### `POST /v1/program-os/lookup-tables/import` — Import (bind) an ALT
- Action: `write` · MCP tool: `program-os.import_lookup_table`
- Persists an ALT binding row after reading the account from chain.
- Request body (JSON, required):
  - `address` — string (required)
  - `cluster` — string
  - `wallet_os_account` — string
  - `display_label` — string
  - `control_mode_hint` — "wallet_os" | "member" | "external" | "frozen"

### `GET /v1/program-os/lookup-tables/{address}` — Get an ALT binding with live chain state
- Action: `read` · MCP tool: `program-os.get_lookup_table`
- Re-reads the ALT from chain, reconciles the binding, and returns capability flags.
- Path param `address` (string, required)

### `DELETE /v1/program-os/lookup-tables/{address}` — Remove an ALT binding
- Action: `write` · MCP tool: `program-os.delete_lookup_table`
- Deletes the binding row only; the on-chain ALT is untouched.
- Path param `address` (string, required)

### `POST /v1/program-os/lookup-tables/prepare-create` — Prepare a CreateLookupTable transaction
- Action: `write` · MCP tool: `program-os.prepare_lookup_table_create`
- Derives the new ALT's address from [authority, recent_slot] and builds the unsigned CreateLookupTable message for the caller's wallet to sign. Returns the derived lookup_table address to extend once the create confirms.
- Request body (JSON, required):
  - `cluster` — string
  - `authority` — string (required)
  - `payer` — string

### `POST /v1/program-os/lookup-tables/{address}/prepare-extend` — Prepare an ExtendLookupTable transaction
- Action: `write` · MCP tool: `program-os.prepare_lookup_table_extend`
- Builds the unsigned ExtendLookupTable message for the caller's wallet to sign.
- Path param `address` (string, required)
- Request body (JSON, required):
  - `cluster` — string
  - `authority` — string (required)
  - `payer` — string
  - `new_addresses` — string[] (required)

### `POST /v1/program-os/lookup-tables/{address}/prepare-freeze` — Prepare a FreezeLookupTable transaction
- Action: `write` · MCP tool: `program-os.prepare_lookup_table_freeze`
- Builds the unsigned FreezeLookupTable message (PERMANENT) for the caller to sign.
- Path param `address` (string, required)
- Request body (JSON, required):
  - `cluster` — string
  - `authority` — string (required)

### `POST /v1/program-os/lookup-tables/{address}/prepare-close` — Prepare a CloseLookupTable transaction
- Action: `write` · MCP tool: `program-os.prepare_lookup_table_close`
- Builds the unsigned CloseLookupTable message that reclaims rent to the recipient.
- Path param `address` (string, required)
- Request body (JSON, required):
  - `cluster` — string
  - `authority` — string (required)
  - `recipient` — string (required)

### `POST /v1/program-os/lookup-tables/{address}/prepare-deactivate` — Prepare a DeactivateLookupTable transaction
- Action: `write` · MCP tool: `program-os.prepare_lookup_table_deactivate`
- Builds the unsigned DeactivateLookupTable message for the caller's wallet to sign.
- Path param `address` (string, required)
- Request body (JSON, required):
  - `cluster` — string
  - `authority` — string (required)

### `GET /v1/program-os/programs/{id}/deployments/{deployment_id}/artifact` — Download a deployment's verified program .so
- Action: `read` · MCP tool: `program-os.download_artifact`
- Returns the deployment's compiled program binary (base64) after re-verifying it against the recorded elf_hash — provably the attested bytes. Source-neutral.
- Path param `id` (string, required)
- Path param `deployment_id` (string, required)

### `GET /v1/program-os/programs/{id}/deploy-preview` — Deploy wizard preview for a program
- Action: `read` · MCP tool: `program-os.deploy_preview`
- Returns the candidate release metadata, on-chain Program/ProgramData state, the upgrade authority + its classification, and the chunk plan the deploy wizard renders first.
- Path param `id` (string, required)
- Query param `candidate_id` (string)

### `POST /v1/program-os/programs/deploy/prepare-buffer-init` — Prepare CreateAccount + InitializeBuffer
- Action: `write` · MCP tool: `program-os.prepare_buffer_init`
- Returns a 2-ix transaction that creates and initializes a BPF Loader buffer account.
- Request body (JSON, required):
  - `cluster` — string
  - `funder` — string (required)
  - `buffer` — string (required)
  - `buffer_authority` — string (required)
  - `elf_size` — integer (required)

### `POST /v1/program-os/programs/deploy/prepare-write-chunks` — Prepare a batch of buffer Write transactions
- Action: `write` · MCP tool: `program-os.prepare_write_chunks`
- Streams the candidate's ELF (tamper-checked) and returns a batch of prepared Write txs for the buffer.
- Request body (JSON, required):
  - `cluster` — string
  - `candidate_id` — string (required)
  - `buffer` — string (required)
  - `buffer_authority` — string (required)
  - `fee_payer` — string
  - `start_chunk` — integer
  - `end_chunk` — integer
  - `chunk_bytes` — integer

### `POST /v1/program-os/programs/deploy/prepare-set-buffer-authority` — Prepare a SetAuthority for a buffer
- Action: `write` · MCP tool: `program-os.prepare_set_buffer_authority`
- Returns a transaction transferring a buffer's authority (e.g. to the wallet_os_account before a multisig Upgrade).
- Request body (JSON, required):
  - `cluster` — string
  - `buffer` — string (required)
  - `current_authority` — string (required)
  - `new_authority` — string (required)

### `POST /v1/program-os/programs/{id}/deploy/prepare-upgrade` — Prepare a direct Upgrade (single-sig)
- Action: `write` · MCP tool: `program-os.prepare_upgrade`
- Returns the UpgradeableLoader::Upgrade transaction signed by the current upgrade authority.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `cluster` — string
  - `upgrade_authority` — string (required)
  - `buffer` — string (required)
  - `spill` — string
  - `fee_payer` — string

### `POST /v1/program-os/programs/{id}/deploy/prepare-deploy` — Prepare a first-time program deploy
- Action: `write` · MCP tool: `program-os.prepare_deploy`
- Returns CreateAccount + DeployWithMaxDataLen for an initial program deployment from a filled buffer.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `cluster` — string
  - `payer` — string (required)
  - `program_id` — string (required)
  - `buffer` — string (required)
  - `upgrade_authority` — string (required)
  - `max_data_len` — integer

### `POST /v1/program-os/programs/{id}/deploy/cosign` — Co-sign the create with the sealed program keypair
- Action: `write` · MCP tool: `program-os.cosign_deploy`
- Signs the FINAL create message (the client's blockhash already stamped) with the sealed one-time-use program keypair and returns the detached signature. The operator still signs with their own wallet. Gated: the message must reference the program account.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `message_base64` — string (required)

### `POST /v1/program-os/programs/deploy/prepare-close-buffer` — Prepare a buffer Close (recovery)
- Action: `write` · MCP tool: `program-os.prepare_close_buffer`
- Returns a transaction closing an abandoned buffer and refunding its rent to the recipient.
- Request body (JSON, required):
  - `cluster` — string
  - `buffer` — string (required)
  - `buffer_authority` — string (required)
  - `recipient` — string (required)

### `POST /v1/program-os/programs/{id}/deploy/prepare-set-upgrade-authority` — Prepare a SetAuthority for a program's upgrade authority
- Action: `write` · MCP tool: `program-os.prepare_set_upgrade_authority`
- Returns a transaction transferring (or freezing) the program's ProgramData upgrade authority.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `cluster` — string
  - `current_authority` — string (required)
  - `new_authority` — string
  - `fee_payer` — string

### `POST /v1/program-os/programs/{id}/deploy/prepare-close-program` — Prepare a program Close (reclaim ProgramData rent)
- Action: `write` · MCP tool: `program-os.prepare_close_program`
- Returns a transaction that closes a deployed program, reclaiming its ProgramData rent to the recipient and permanently retiring the program id. Upgradeable programs only — a frozen program has no authority to sign.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `cluster` — string
  - `current_authority` — string (required)
  - `recipient` — string (required)
  - `fee_payer` — string

### `GET /v1/program-os/programs/{id}/deploy/upgrade-payload-hash` — Canonical multisig upgrade payload hash
- Action: `read` · MCP tool: `program-os.upgrade_payload_hash`
- Returns the canonical payload hash for a planned multisig upgrade, fed into the kernel create_action + execute_program_upgrade flow.
- Path param `id` (string, required)
- Query param `candidate_id` (string, required)
- Query param `buffer` (string, required)
- Query param `spill` (string)

### `POST /v1/program-os/programs/{id}/deploy/prepare-attest` — Prepare the on-chain ProgramDeployment attestation (signable tx)
- Action: `write` · MCP tool: `program-os.prepare_attest_deployment`
- Builds the create_program_deployment instruction that commits the candidate's manifest/ELF/IDL/source-commit hashes to the attestation program. Non-custodial: returns the signable message; the operator signs + submits.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `candidate_id` — string (required)
  - `authority` — string (required)

### `POST /v1/program-os/programs/{id}/deploy/reconcile-candidate` — Reconcile a candidate against on-chain state
- Action: `write` · MCP tool: `program-os.reconcile_candidate`
- Reads ProgramData from chain, hashes the ELF, compares to the candidate's recorded elf_hash, and promotes the candidate to current on a match.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `candidate_id` — string (required)

### `GET /v1/program-os/programs/{id}/candidates/{cid}/idl` — Stream a candidate's IDL JSON
- Action: `read` · MCP tool: `program-os.candidate_idl`
- Streams the tamper-checked IDL JSON for a deployment candidate from its GitHub release asset.
- Path param `id` (string, required)
- Path param `cid` (string, required)

### `GET /v1/program-os/programs/{id}/accounts` — List program-owned accounts
- Action: `read` · MCP tool: `program-os.list_accounts`
- Lists every on-chain account owned by this program, with the 8-byte discriminator for IDL matching.
- Path param `id` (string, required)
- Query param `limit` (integer)
- Query param `data_size` (string)
- Query param `discriminator_hex` (string)
- Query param `cursor` (string)

### `GET /v1/program-os/programs/{id}/accounts/{address}` — Get a program-owned account
- Action: `read` · MCP tool: `program-os.get_account`
- Full account data for one program-owned account (lamports, owner, base64 data bytes).
- Path param `id` (string, required)
- Path param `address` (string, required)

### `GET /v1/program-os/programs/{id}/token-accounts` — List token accounts for program authorities
- Action: `read` · MCP tool: `program-os.list_token_accounts`
- Lists SPL token accounts (classic + Token-2022) and SOL balances for the wallet_os_account and upgrade authority.
- Path param `id` (string, required)

### `GET /v1/program-os/programs/{id}/buffers` — List buffers staged for this program
- Action: `read` · MCP tool: `program-os.list_buffers`
- Lists BPF Loader buffer accounts whose authority matches the program's current authority or wallet_os_account (60s cached).
- Path param `id` (string, required)

### `GET /v1/program-os/programs/{id}/logs` — Recent on-chain invocations
- Action: `read` · MCP tool: `program-os.list_logs`
- Returns the most-recent signatures touching this program (status, slot, block time), paginated.
- Path param `id` (string, required)
- Query param `limit` (integer)
- Query param `cursor` (string)
- Query param `before` (string)

### `GET /v1/program-os/programs/{id}/analytics` — Program volume + reliability analytics
- Action: `read` · MCP tool: `program-os.get_analytics`
- Transaction volume, success/failure counts, error rate, active time span, and a per-day series over a recent signature window (read live from chain).
- Path param `id` (string, required)
- Query param `window` (integer)

### `GET /v1/program-os/env-vars` — List env vars (secrets masked, never revealed)
- Action: `read` · MCP tool: `program-os.list_env_vars`
- Returns plain variables with their value and secrets with a masked placeholder + metadata only. NEVER returns a secret value or ciphertext. Project scope folds in workspace-shared vars; project-level keys override workspace-level.
- Query param `project_id` (any)
- Query param `cluster` ("mainnet-beta" | "devnet" | "testnet")
- Query param `include_workspace` (boolean)

### `POST /v1/program-os/env-vars` — Create an env var or write-only secret
- Action: `write` · MCP tool: `program-os.create_env_var`
- Stores a build-time variable (plain, readable) or secret (sealed write-only to a public key the gateway holds — the value can never be read back, not by any user, admin, or API). Scope is a project, or workspace-shared (project_id omitted). Per environment.
- Request body (JSON, required):
  - `key` — string (required)
  - `value` — string (required)
  - `clusters` — "mainnet-beta" | "devnet" | "testnet"[] (required)
  - `type` — "plain" | "secret" (required)
  - `project_id` — any
  - `target_project_ids` — string[]

### `PATCH /v1/program-os/env-vars/{id}/value` — Replace an env var's value (new version)
- Action: `write` · MCP tool: `program-os.replace_env_var_value`
- Writes a new version; the previous one is retired and purged after retention. For a secret the new value is sealed write-only — still never readable.
- Path param `id` (string, required)
- Request body (JSON, required):
  - `value` — string (required)

### `DELETE /v1/program-os/env-vars/{id}` — Delete an env var
- Action: `write` · MCP tool: `program-os.delete_env_var`
- Marks the variable deleted immediately (the next build will not inject it); ciphertext is purged by cron after retention. Audit metadata is kept.
- Path param `id` (string, required)
