Repository navigation
Conversation
…ra org provenance (ankra-q573dh.44) Drives the verified-provenance routes from cluster#4611: the per-application mode, the organisation default, the dry-run verify, and the signing keys (list, rotate, revoke). Every place that shows or turns on enforce says that versions published before the first attested publish cannot be deployed or rolled back to, and how to switch to warn. Turning enforce on and revoking a key ask first. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ankra AI reviewVerdict: comments. Well-structured addition of provenance policy and key-management commands with thorough tests, confirmation prompts on destructive actions, and careful handling of unknown-vs-refused verification states. No clear defects in the diff itself; two findings depend on helpers defined outside the diff. CI is still running, so build status is unknown. Findings
Reviewed commit c1f6de0. This review is read-only and advisory. |
|
|
||
| func printOrganisationProvenancePolicy(out io.Writer, policy *client.OrganisationProvenancePolicy) { | ||
| _, _ = fmt.Fprintf(out, " Organisation default: %s\n", provenanceModeLabel(policy.Mode, | ||
| "not set (applications use Ankra's default: off for applications created before 2026-10-14, warn since)")) |
There was a problem hiding this comment.
[low] Hardcoded platform-default date in user-facing output
The 'not set' fallback text hardcodes Ankra's default ('off for applications created before 2026-10-14, warn since'). This is a hardcoded claim about server-side behaviour that will silently go stale if the platform default or cutoff changes, and users will trust it as a current observation. Consider having the API return the effective default (or its description) so the CLI reports what the platform actually does rather than what it did when this string was written.
Ankra AI review. Read-only and advisory.
There was a problem hiding this comment.
Fixed in the next push: the fallback text no longer states the platform's default date.
| return withExitCode(exitNotFound, fmt.Errorf("%s: %w", operation, apiError)) | ||
| } | ||
| } | ||
| if hasStatus && unexpected.StatusCode == http.StatusServiceUnavailable { |
There was a problem hiding this comment.
[low] 503 error message names keys even for policy operations
The 503 branch always says 'the provenance keys could not be read right now', but provenanceError is also used for policy reads, policy writes, verify, rotate and revoke. A 503 during 'org provenance set' would tell the user the keys could not be read, which misdescribes what failed. Use the operation string (already passed in) to describe the failure, or reserve the keys wording for the keys/rotate/revoke call sites.
Ankra AI review. Read-only and advisory.
There was a problem hiding this comment.
Fixed in the next push: the 503 message now uses the operation string and no longer names keys.
| } | ||
| if hasStatus && unexpected.StatusCode == http.StatusNotFound { | ||
| switch unexpected.Detail { | ||
| case "", routeAbsentDetail: |
There was a problem hiding this comment.
[low] Undefined symbols routeAbsentDetail and UnexpectedResponseError.Detail
This relies on routeAbsentDetail and on UnexpectedResponseError having a Detail field, neither of which is shown in the diff. If Detail is not populated for router 404s (e.g. only set for JSON error bodies), the old-platform detection could misfire: a bare 404 with an empty or unparsed body would be reported as 'platform does not offer provenance' even when the route exists but the application id was wrong. Confirm the router's 404 body shape matches the routeAbsentDetail constant.
Ankra AI review. Read-only and advisory.
There was a problem hiding this comment.
Both exist in the repo, outside the diff: routeAbsentDetail = "Not Found" in cmd/targets.go (the router's own 404 body) and UnexpectedResponseError.Detail in internal/client/unexpected_response.go, which is set only when the backend sent a JSON detail. A handler's 404 (unknown application or key) carries its own sentence, so it exits 3 and is not reported as an old platform; only an empty or "Not Found" detail is. The same mapping is used by control-plane-heal and hosted logs, and TestApplicationProvenanceOldPlatformIsNamed pins it.
| prompt := fmt.Sprintf("Turn enforce on for %q? Deploys are then refused unless every image is attested, and "+ | ||
| "versions published before the first attested publish cannot be deployed or rolled back to "+ | ||
| "(switch to warn to lift that). [y/N]: ", arguments[0]) | ||
| if confirmError := confirmPrompt(command.InOrStdin(), command.OutOrStdout(), prompt, yes); confirmError != nil { |
There was a problem hiding this comment.
[low] confirmPrompt and resolveApplicationArgument signatures assumed
confirmPrompt is called as (io.Reader, io.Writer, string, bool) and resolveApplicationArgument as (command, arguments); neither is defined in the diff. If confirmPrompt does not treat EOF on stdin as a decline, a non-interactive run without --yes could block or behave unexpectedly on the enforce path. Verify the helper declines (rather than errors or hangs) when stdin is closed.
Ankra AI review. Read-only and advisory.
There was a problem hiding this comment.
confirmPrompt (cmd/cluster_partial.go:697) reads one line; on EOF the line is empty, which is not y/yes, so it returns errCancelled (exit 4) rather than blocking or proceeding. Closed stdin without --yes therefore declines. TestApplicationProvenanceSetEnforceAsksFirst covers a declined prompt sending nothing.
… the operation Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
| } | ||
| if hasStatus && unexpected.StatusCode == http.StatusNotFound { | ||
| switch unexpected.Detail { | ||
| case "", routeAbsentDetail: |
There was a problem hiding this comment.
[medium] Referenced symbols not visible in the diff
provenanceError relies on routeAbsentDetail, client.PermissionDeniedError, and client.UnexpectedResponseError (with a Detail field), and the command files rely on confirmPrompt, resolveApplicationArgument, renderStructured, structuredFormatFromFlags, registerStructuredOutputFlags, withExitCode, and the exit* constants, none of which are declared in this diff. If any of these do not exist with the assumed shapes (e.g. UnexpectedResponseError lacking a Detail string, or routeAbsentDetail not matching the router's 404 body), the package will not compile or the old-platform 404 branch will never trigger. I could not see their declarations, so please confirm they exist as assumed.
Ankra AI review. Read-only and advisory.
There was a problem hiding this comment.
Same answer as the earlier thread: all of these helpers already exist in the repo outside the diff (cmd/targets.go routeAbsentDetail, cmd/cluster_partial.go confirmPrompt, cmd/output.go, cmd/exitcodes.go) and the package compiles and passes tests and lint in CI.
| return renderError | ||
| } | ||
| out := command.OutOrStdout() | ||
| if onlyPEM, _ := command.Flags().GetBool("public-keys"); onlyPEM { |
There was a problem hiding this comment.
[low] --public-keys prints nothing when no keys exist
When the organisation has no keys, the table path prints a helpful 'No provenance key exists yet' message, but the --public-keys path silently prints nothing and exits 0. A script doing keys --public-keys > provenance.pub then feeds an empty file to cosign with no hint why. Consider writing the same note to stderr when the list is empty so the empty stdout stays machine-consumable but the user is not left guessing.
Ankra AI review. Read-only and advisory.
There was a problem hiding this comment.
Fixed: with no keys, --public-keys now writes the same note to stderr (stdout stays empty, exit 0).
…resolve it Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
| prompt := fmt.Sprintf("Turn enforce on for %q? Deploys are then refused unless every image is attested, and "+ | ||
| "versions published before the first attested publish cannot be deployed or rolled back to "+ | ||
| "(switch to warn to lift that). [y/N]: ", arguments[0]) | ||
| if confirmError := confirmPrompt(command.InOrStdin(), command.OutOrStdout(), prompt, yes); confirmError != nil { |
There was a problem hiding this comment.
[low] confirmPrompt behaviour on non-interactive stdin is unverified
Turning on enforce (and revoking a key) relies on confirmPrompt failing closed when stdin is not interactive and --yes was not passed (e.g. in a CI script). confirmPrompt is not shown in this diff; if it treats EOF as 'yes' or blocks forever, a script could either hang or silently enable enforce. Please confirm it returns a cancellation error on EOF/no input.
Ankra AI review. Read-only and advisory.
There was a problem hiding this comment.
confirmPrompt (cmd/cluster_partial.go:697) treats anything but y/yes, including EOF, as errCancelled (exit 4). It cannot hang on closed stdin and cannot default to yes. TestApplicationProvenanceSetEnforceAsksFirst and TestOrgProvenanceRevokeConfirmsAndSendsReason pin the decline path.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
What
CLI surfaces for verified provenance at deploy (cluster#4611):
ankra application provenance get|set|verify <app>: effective mode and its source, set--mode off|warn|enforceor--clear, and a dry-runverify --image <ref>...that exits 1 when a deploy would be refused.ankra org provenance get|set|keys|rotate|revoke: organisation default, signing keys (keys --public-keysprints PEM forcosign verify-attestation).enforceevery command that shows or turns it on says older un-attested versions cannot be deployed or rolled back to, and the exact command to switch towarn. Enabling enforce and revoking a key confirm first (--yes).security.manage(exit 7 without it); an older platform's bare 404 is named; the stored mode is compared with the request.Tests
18 new tests against an httptest platform (method, path, body), including
-o jsonstdout cleanliness, declined prompts sending nothing, and usage errors. Mutation check: breaking mode validation fails 5+ tests; restored. Fullgo test ./...and golangci-lint pass in the pre-commit hook.CHANGELOG entry under Unreleased.
Bead: ankra-q573dh.44
🤖 Generated with Claude Code