Cloud Todo Boundary

Propodus is the hosted authentication and authorization boundary; Dactyl is the physical storage boundary. They are not interchangeable. repo.backend = "cloud" selects the cloud backend: Decapod authenticates through Propodus, opens Dactyl with a versioned storage context, and never silently falls back to local SQLite.

Dactyl storage contract

The Decapod bridge constructs local Dactyl SQLite or cloud Supabase HTTP routes by default; Neon HTTP remains an explicitly selected alternative. It does not set or trust ambient DATASTORE, DATASTORE_ROUTE, or DATASTORE_TOKEN during connection construction. The cloud route receives an opaque machine-session bearer and the versioned repository context. Individual SQL operations do not select a provider or interpret authorization.

The Supabase HTTP capability, enabled in standard builds, is described in Supabase consumer validation. Decapod consumes the published dactyl-db 0.11.1 registry release containing the client reviewed in Dactyl #92. The committed lockfile supplies its registry checksum; registry publication is not hosted service proof. Cloud selects Supabase unless DECAPOD_CLOUD_DATASTORE=neon is set. Local SQLite behavior is unchanged. Supabase still requires a configured compatible authenticated service endpoint; no production deployment is implied.

OperationDactyl requestScope/authentication
Read/listPOST /querySQL plus opaque versioned context
Add/claim/release/completePOST /batchordered Dactyl operations plus context

The bearer is sent in the HTTP Authorization header by Dactyl. The context is forwarded as an opaque JSON object; Propodus resolves its authenticated principal and repository authorization. Add, claim, release, and complete use a Dactyl batch containing the conditional state write, the matching event write, and a task observation. The event insert is conditioned on the state transition marker. The service must also require a successful preceding state mutation before writing an event, including when a stale request reuses a timestamp marker; a timestamp alone is not authorization for an event.

The checked-in tests/cloud_dactyl_boundary.rs proof verifies /query, the bearer header, the versioned context, and the absence of a per-query backend field without contacting Vercel or Neon. The older Propodus fixture and propodus_contract test remain compatibility coverage for the legacy /api/todos client; they are not the active Decapod storage implementation.

The deployed onboarding/session shape is recorded separately in tests/fixtures/propodus/onboarding-contract-v1.json: Decapod starts with the canonical repo_id, prints/opens the one-time URL, polls status, consumes the ready flow once, exchanges the returned code for a machine session, and rotates that session through the refresh route. Credentials stay outside the repository.

The command boundary is covered by tests/cloud_command_path.rs, which proves that list, add, get, claim, release, and complete are routed through the backend-neutral TodoStore adapter. Unsupported local-only operations return an explicit error on the cloud backend.

The production-dispatch proof is tests/cloud_cli_boundary.rs; it uses a mock Dactyl store factory and exercises the same run_todo_cli composition used by the binary. It proves config discovery, canonical-origin validation, credential preflight, list/add/get/show/claim/done routing, not-found behavior, and the absence of local SQLite initialization for cloud todo commands.

The opt-in live proof is tests/propodus_live.rs. Run it only with DECAPOD_PROPODUS_LIVE=1, DECAPOD_PROPODUS_API_URL, DECAPOD_PROPODUS_ACCESS_TOKEN, and a disposable DECAPOD_PROPODUS_DISPOSABLE_REPO_ID:

DECAPOD_PROPODUS_LIVE=1 \
DECAPOD_PROPODUS_API_URL=https://your-stable-propodus.example \
DECAPOD_PROPODUS_ACCESS_TOKEN=... \
DECAPOD_PROPODUS_DISPOSABLE_REPO_ID=example/decapod-live-deny \
cargo test --test propodus_live -- --ignored --nocapture

The proof creates one uniquely named todo in the canonical repository, claims it, completes it, and verifies that the disposable repository receives 403 repository_not_authorized. It also verifies that an invalid bearer token is rejected with a 401 authentication failure. The command-level proof also uses two Decapod agents to verify shared visibility and rejects a fork before any request is sent. It does not delete the sentinel because the v1 client contract has no delete operation; remove it with Propodus operator tooling after the run. The test is ignored by default, and the CI job is manual, environment-protected, and gated by the DECAPOD_PROPODUS_LIVE repository variable.

Propodus also uses 403 organization_seat_required when a valid GitHub bearer token lacks the required organization seat for the canonical repository.

Backend-neutral storage context

Decapod's versioned core::backend::StorageContext is the handoff from logical backend selection to a physical driver. A local context contains only the repository-local SQLite route and has no organization, user, or cloud repository fields. A remote context contains the opaque service route and the logical repository scope derived from origin; it requires an authenticated session bearer, but the bearer is memory-only and is omitted from serialized context data.

The Dactyl bridge forwards the explicit route, versioned context envelope, and opaque bearer without interpreting membership or authorization. The bearer never enters ambient Dactyl environment variables. Context debug formatting redacts the bearer and endpoint; serialization excludes the bearer. The versioned context carries the target org/repo scope unchanged. Propodus remains responsible for resolving the authenticated principal, organization membership, and repository access. The Decapod bridge opens the canonical local decapod.db directly through Dactyl's local adapter and opens the cloud route through Dactyl's Supabase adapter by default (or explicit Neon); it does not create a snapshot or bundled compatibility database. Existing SQLite state is inspected and migrated by Decapod through the same Dactyl-backed facade. This keeps one canonical Dactyl authority and prevents cloud operations from silently falling back to local storage. See Decapod #1254, Dactyl #64, and Propodus #79.

Credentials

Credentials are never read from .decapod/config.toml. Lookup precedence is:

  1. an explicit client credential;
  2. DECAPOD_ACCESS_TOKEN for controlled development and CI use;
  3. the selected endpoint-scoped machine session for Supabase, or the legacy ~/.local/share/decapod/session_token.json only for explicit Neon.

cloud status checks the same selected session store without network I/O.

Run decapod init --backend cloud as the only human-facing cloud setup step. It starts or resumes the repository-bound browser handoff through Decapod's baked-in Propodus endpoint. Agents acquire custody with decapod session acquire, which reuses or refreshes the machine-local session without asking for another human action. Use decapod cloud status only to check whether a bearer is configured without printing the token. The deprecated cloud login compatibility alias remains for existing scripts and is not part of the setup flow. Interactive terminals open the URL when a browser launcher is available and always print it as a fallback; headless callers receive a bounded resume instruction. A completed exchange is stored machine-locally with restrictive permissions and expired sessions refresh through the provider. Bearer tokens are sent only in the Authorization header; Decapod never logs or writes them to repository configuration, generated governance, URLs, or commits.

Repeatable dogfood setup

From a fresh checkout of the canonical repository:

  1. Run decapod init --backend cloud --proof and confirm .decapod/config.toml contains only the backend selection repo.backend = "cloud" for cloud composition. The Propodus endpoint and provider are binary-owned; no service URL or project identifier is stored in project configuration.
  2. Ensure origin is an unambiguous GitHub remote. Decapod derives the canonical owner/name from it and sends that binding to the provider; the provider decides whether the authenticated session may use the repository.
  3. Run decapod todo list, add, get, show, claim, or done. If no machine session exists, first complete decapod init --backend cloud; the initialized machine state then resumes the one-time browser handoff on the next invocation. A controlled DECAPOD_ACCESS_TOKEN remains available for protected proofs.
  4. Cloud todo commands use the same TodoStore command boundary as local todo commands, but compose Dactyl storage with the Propodus-authenticated context instead of local SQLite. They do not acquire the local agent session or create/migrate the local todo database. Missing credentials or a canonical GitHub remote fail closed and never fall back to SQLite.

Repository identity

The cloud backend derives a canonical owner/name binding from the origin remote and rejects non-GitHub or ambiguous remotes. Forks remain distinct identities and are passed to the provider for authorization; Decapod does not maintain a repo allowlist or accept a project-configured repository identifier.

Current governance limits

The cloud Dactyl todo slice supports repo-scoped list/add/get/claim/release/ complete operations. get and show use keyed reads through Dactyl rather than list-and-filter. Add, claim, release, and complete each use one Dactyl atomic batch for the conditional task transition, matching event, and final task observation. A compatible service must enforce that a zero-row task transition cannot create an event, even if a timestamp marker matches an older operation. Client checks of returned counts occur after the remote transaction and cannot alone enforce rollback. The Supabase service proof tests this server-side requirement; timestamps alone are not compare-and-swap authority. A missing item returns status = "not_found". todo done --validated is intentionally rejected because v1 has no proof capture or verification-artifact contract. This is an explicit unsupported boundary, not full remote governance completion; a future proof-contract issue must define the service-side evidence model before Decapod can compose it; see Decapod issue #1038.

Delivery boundary

Local SQLite maintenance

Local operators can inspect and maintain the canonical store explicitly:

decapod data db verify
decapod data db backup --destination /same-directory-or-approved-backup/decapod.backup.db
decapod data db recover --preserve-original-at /same-directory/decapod.before-recovery.db

verify is read-only and calls Dactyl v0.10.0's typed native integrity verification. backup uses Dactyl's SQLite online-backup contract, including WAL/SHM correctness, and publishes a standalone destination after verification. recover is an explicit operator action only: it invokes Dactyl's logical dump/reload workflow, preserves the original at the requested unused sibling path, and atomically replaces the active database only after the replacement passes verification. Dactyl reports rollback or recovery failures without silently repairing the store. The recovered database uses DELETE journal mode and preserves application rows, schema, user_version, and application_id.

The canonical Decapod sidecar lock is held for each operation. Recovery also requires all cooperating Decapod writers and same-process Dactyl connections to be quiesced; backup requires normal cooperating-client coordination. The archive must not already exist and must be on the same filesystem where Dactyl can perform its atomic replacement. The sidecar does not coordinate arbitrary external SQLite writers or make unreliable network/container mounts safe. Lock contention, unavailable runtimes, malformed/corrupt stores, unsupported capabilities, and recovery/rollback failures remain explicit diagnostic outcomes. Decapod never runs repair during startup, validation, event append, or connection creation.

This Decapod slice activates the explicit cloud todo command path, remote-derived repository identity, Propodus onboarding/session exchange, machine-local refresh, Dactyl 0.10.0 adapter-level command and event-atomicity proof, and a wire-level /query proof without moving hosted authentication, repository authorization, persistence, or deployment into Decapod. Local mode continues to use the same backend-neutral todo command boundary, with physical canonical storage owned by Dactyl. Live Neon/Vercel availability, Propodus/Dactyl service deployment, cross-organization isolation, schema/migration parity, and hosted event atomicity remain deployment-dependent proof gates; the client-side batch contract and local rollback proof do not claim those hosted properties.

For interrupted cloud creation and auth-blocked custody, see Resuming cloud todo creation. A returned operation token is a retry identity, never an authorization grant or local task-store fallback.