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.
| Operation | Dactyl request | Scope/authentication |
|---|---|---|
| Read/list | POST /query | SQL plus opaque versioned context |
| Add/claim/release/complete | POST /batch | ordered 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:
- an explicit client credential;
DECAPOD_ACCESS_TOKENfor controlled development and CI use;- 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:
- Run
decapod init --backend cloud --proofand confirm.decapod/config.tomlcontains only the backend selectionrepo.backend = "cloud"for cloud composition. The Propodus endpoint and provider are binary-owned; no service URL or project identifier is stored in project configuration. - Ensure
originis 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. - Run
decapod todo list,add,get,show,claim, ordone. If no machine session exists, first completedecapod init --backend cloud; the initialized machine state then resumes the one-time browser handoff on the next invocation. A controlledDECAPOD_ACCESS_TOKENremains available for protected proofs. - Cloud todo commands use the same
TodoStorecommand 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.