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 client uses Dactyl dactyl-db 0.9.0. Decapod supplies Dactyl's ambient route values at connection construction: DATASTORE=sqlite or DATASTORE=neon, DATASTORE_ROUTE for the local file or the hardcoded Propodus Vercel/Neon origin (https://project-oqn7i.vercel.app), and DATASTORE_TOKEN for the machine session bearer. Dactyl resolves those values and runs the same operation contract for either backend; individual SQL operations do not receive a backend selector, provider name, tenant argument, or other out-of-band query input.

At route binding, Decapod establishes Dactyl's ambient process inputs: DATASTORE=sqlite or DATASTORE=neon, the opaque DATASTORE_ROUTE, and the cloud-only DATASTORE_TOKEN. Selecting local clears any stale cloud token. Dactyl resolves these values with DatastoreRoute::from_env() when Decapod opens a connection; no provider-specific route logic or backend selector is added to the execution API.

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, so a lost claim or stale completion aborts the entire batch instead of committing state without its 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 v0.9.0 bridge forwards the route, versioned context envelope, and opaque bearer without interpreting membership or authorization. Decapod keeps the bearer in Dactyl's ambient DATASTORE_TOKEN only while Dactyl captures the connection route; the token is restored/removed from the process afterward. 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 Neon adapter; 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 machine-local ~/.local/share/decapod/session_token.json.

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 stale transition fails before either state or event is committed. 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

This Decapod slice activates the explicit cloud todo command path, remote-derived repository identity, Propodus onboarding/session exchange, machine-local refresh, Dactyl 0.9.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.