Skip to content

Add --connect-sign flag to avocado deploy#158

Open
jetm wants to merge 9 commits into
mainfrom
connect-sign
Open

Add --connect-sign flag to avocado deploy#158
jetm wants to merge 9 commits into
mainfrom
connect-sign

Conversation

@jetm

@jetm jetm commented Jun 24, 2026

Copy link
Copy Markdown

Problem

Devices that have received a Connect OTA update trust the Connect server key
as their TUF root, so locally-signed TUF metadata is rejected. avocado deploy
has no way to sign metadata via Connect, leaving these devices stuck.

Solution

Add --connect-sign to both avocado deploy entry points. When set, Phase 2
routes through a new ConnectClient::sign_for_deploy method that POSTs the
target list to /api/orgs/{org}/signing/sign-for-deploy and writes the four
returned JSON blobs to staging. The local path is unchanged; --connect-sign
is an explicit opt-in.

A nudge prints on local deploys when the project has connect.server_key set,
surfacing the flag without forcing it.

Key changes

  • TargetFileInfo gains Serialize so the targets array goes directly into the request body
  • ConnectClient::sign_for_deploy and response types added, mirroring the get_tuf_server_key pattern
  • Phase 2 in deploy.rs branches on --connect-sign: resolves org early, calls the API, writes the four TUF JSON files, prints 7-day expiry notice
  • Phase 2 progress label switches to "Signing TUF metadata via Connect..." under --connect-sign
  • root.json write moved outside the if/else so both signing paths stage it
  • Flag wired to both Commands::Deploy and RuntimeCommands::Deploy in main.rs
  • config show --output json now emits a per-runtime signing_enabled boolean so Avocado Desktop can gate "Sign via Connect" (unblocks ENG-2005)
  • .pre-commit-config.yaml added: cargo fmt/clippy/check on commit, cargo audit on push

Reviewer notes

Gated on ENG-1963 — the sign-for-deploy endpoint is not live yet. Unit
tests pass; a live E2E needs three things in place:

  1. ENG-1963 endpoint landed and reachable
  2. A device that has taken a Connect OTA (rejects local signing as baseline)
  3. An authenticated Connect session with a valid org

E2E sequence once ready:

# Confirm baseline failure
avocado deploy -r <runtime> -d <device-ip>

# Connect-signed deploy
avocado deploy --connect-sign -r <runtime> -d <device-ip>
# -> success + 7-day expiry notice; device applies without TUF errors

This PR also closes the ENG-2005 config-show contract: config show --output json
exposes signing_enabled per runtime, resolved identically to the deploy nudge
(per-runtime signing.server_key, falling back to connect.server_key).

No new dependencies introduced.

jetm added 9 commits June 24, 2026 16:40
TargetFileInfo could only be deserialized, making it impossible to
serialize the struct to JSON or other formats without manual
conversion. This becomes a blocker when TargetFileInfo instances
need to be passed across API boundaries or stored in serialized form
as part of a larger workflow.

Derive Serialize alongside the existing Deserialize implementation
so that TargetFileInfo supports full roundtrip serialization. This
is the minimal, idiomatic Rust approach and avoids duplicating field
mapping logic elsewhere in the codebase.

Signed-off-by: Javier Tia <javier@peridio.com>
The CLI has no way to request TUF metadata signing from the Connect
platform during a deploy flow. Without this, the client cannot obtain
signed targets, snapshot, and timestamp metadata required to publish
a valid TUF repository update through the Connect signing service.

Introduce the request and response types for the sign-for-deploy
endpoint and implement the corresponding async method on ConnectClient.
The method posts a list of target file descriptors to the per-org
signing endpoint and returns the four signed metadata blobs the caller
needs to assemble a complete TUF repository. Unit tests cover both
serialization of the request payload and deserialization of the
four-field response envelope.

Signed-off-by: Javier Tia <javier@peridio.com>
When a project is managed through the Connect platform, locally-signed
TUF metadata can be rejected by devices that have already received a
Connect OTA update, because those devices trust keys held server-side
rather than the local development key. There was previously no way to
produce a compatible signed deploy without bypassing the CLI entirely.

Introduce a `--connect-sign` flag on the deploy command that routes
Phase 2 TUF metadata signing through the Connect signing endpoint
instead of resolving a local key. When set, the command authenticates
against the configured Connect org, submits the collected targets to
the remote signing API, and writes the returned signed metadata blobs
to the staging directory. The local key resolution and dev-key
auto-generation paths are skipped entirely in this mode. Additionally,
when signing locally, a warning is emitted if the project already has
a Connect server key configured, alerting the user that the resulting
deploy may be incompatible with devices enrolled in Connect.

Signed-off-by: Javier Tia <javier@peridio.com>
Devices that have received a Connect OTA update require TUF metadata
to be signed through the Connect platform rather than locally. Without
this capability, users have no way to deploy to such devices using the
standard deploy workflow.

Expose a --connect-sign boolean flag on both the top-level and runtime
deploy subcommands. When set, signing is delegated to the Connect
platform instead of performing local signing. This matches the trust
model expected by Connect-managed devices and avoids the need for
local key material that may not be available in that deployment context.

Signed-off-by: Javier Tia <javier@peridio.com>
…ruct

SignForDeployRequest<'a> was the only borrowed request struct in the file.
Every sibling method either uses an owned typed struct or an inline
serde_json::json! body for small one-field payloads. The lifetime added
no value: the struct was constructed and consumed in a single call. Use
json! to match the commit_promote_root sibling pattern and remove the
redundant type and its now-trivial serialization test.
CI caught formatting drift in client.rs and deploy.rs introduced by the
connect-sign changes.

Signed-off-by: Javier Tia <javier@peridio.com>
Mirrors the CI check set locally so formatting and clippy failures are
caught before pushing. cargo-audit runs at pre-push stage only (network
call, slow) while fmt/clippy/check run on every commit.

Signed-off-by: Javier Tia <javier@peridio.com>
The ENG-1964 spec calls for the Phase 2 progress line to read "Signing
TUF metadata via Connect..." when --connect-sign is set, but the label
was hardcoded to the local-signing text on both paths. A user running a
Connect-signed deploy saw a line claiming local generation.

Branch the human-readable label on self.connect_sign. The machine-facing
phase event name stays metadata-sign so NDJSON consumers are unaffected.

Signed-off-by: Javier Tia <javier@peridio.com>
Avocado Desktop gates its "Sign via Connect" option by reading
`avocado config show --output json`, but the output exposed no
signing-key signal: the connect block carried only org and project, so
the desktop could not tell whether Connect signing applied. ENG-2005 was
blocked on this contract.

Add a signing_enabled boolean to each runtime entry, resolved through
get_server_key_for_runtime so it agrees exactly with the deploy nudge:
true when the runtime sets its own signing.server_key or inherits the
project-level connect.server_key. A per-runtime signal avoids the
false-negative a project-level boolean would give a runtime that pins
its own key without a global fallback.

Signed-off-by: Javier Tia <javier@peridio.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant