Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2a3bd64
docs: define secret output declaration verification contract
highb Oct 22, 2026
528830d
feat(actions): declare and acknowledge secret output content types
highb Oct 22, 2026
9a55025
chore(proto): generate secret content type declaration
highb Oct 22, 2026
a680c62
test(actions): preserve singleton JWE output transport
highb Oct 22, 2026
da275f1
docs(actions): record typed output regression evidence
highb Oct 22, 2026
4483426
fix(actions): snapshot advertised secret output schemas
highb Oct 23, 2026
899cb2f
docs(actions): record schema snapshot regression proof
highb Oct 23, 2026
b4c6a5b
fix(actions): satisfy gocritic and gosec in typed secret output tests
highb Oct 23, 2026
c65a6c4
Merge tag 'v0.42.4' into highb/connector-declared-secret-types
highb Oct 26, 2026
490593b
Merge main into output declarations
highb Oct 27, 2026
76d6561
Reject typed actions with multiple secret returns
highb Oct 27, 2026
b008f89
Merge main into typed action declarations
highb Oct 27, 2026
a93ec56
Merge released Pebble storage changes into typed outputs
highb Oct 28, 2026
864fc90
Merge current main into typed secret outputs
highb Oct 30, 2026
32121b0
Clarify secret content type configuration semantics
highb Oct 30, 2026
020da74
Regenerate clarified secret content type comments
highb Oct 30, 2026
24b852f
Add content_type and tags to fields.
ggreer Nov 2, 2026
21fe49f
Use shared field metadata for secret outputs
highb Nov 2, 2026
1a072f7
Regenerate shared field metadata protobufs
highb Nov 2, 2026
5bae0e6
Format shared field metadata proto
highb Nov 2, 2026
5980f24
Merge field metadata source from Geoff
highb Nov 3, 2026
69b9808
Regenerate shared field metadata comments
highb Nov 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/verification/connector-secret-types/evidence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Evidence ledger

Base: released v0.42.3, commit 2e045d482d592a9462b5f87c70a6113fe87ec54a. Source declaration/guard: 528830db; generated protobuf: 9a550250; immutable advertised schema correction: 4483426d. No release is claimed.

| Criterion | Executable artifact and assertion | Status |
| --- | --- | --- |
| Declaration presence and schema round-trip | TestSecretContentTypeSchemaRoundTrip, binary/JSON equality and HasSecretContentType absent/present; explicit empty ValidateAll refusal | Verified to stated coverage |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: This ledger (and plan.md change order 1) still describes HasSecretContentType and config.Field.secret_content_type=8, and it cites pre-squash commit hashes. None of those exist in this PR. The current design is content_type (8) plus a multipass-type- entry in tags (9), resolved by crypto.ResolveFieldOutput. Please update these references or remove them.

| Illegal declarations | TestSecretContentTypeRegistrationRejectsInvalidDeclarations, global/resource and ordinary/encrypted registration; no published action on invalid input/public/empty/grammar/length | Verified to stated coverage |
| Pre-handler downgrade prevention | TestTypedSecretOutputRefusesUnacknowledgedCallerBeforeHandler, global/resource; missing/opaque/generic/wrong type/purpose/version/ambiguous/invalid JSON plus unsupported recipients/cardinality; empty action ID, handler counter zero, no allocation | Verified to stated coverage |
| Exact structured bytes and immutable registration type | TestTypedSecretOutputEncryptsExactHelperBytesWithFrozenDeclaration, actual manager/helper/JWE; mutate original schema after registration; independently open X-Wing HPKE bytes and compare canonical username/password JSON | Verified to stated coverage |
| Legacy generic compatibility | TestGenericSecretOutputKeepsOpaqueContexts plus existing ActionHandlerWithSecrets tests; absent/explicit generic and old age multi-output/recipient paths | Verified to stated coverage |
| Instrument adequacy | Removed new guard temporarily, ran account/generic regression: failed because expected error was nil; restored source byte-for-byte | Verified bounded mutation |
| Source generation | make protofmt; make protogen exit0. Only config.pb.go, config.pb.validate.go, config_protoopaque.pb.go changed; generated separately | Verified |
| Full package and race | go test -race -v ./pkg/actions -count=1; all named package tests ran/pass, no skips; 11.238s final package execution | Verified to stated coverage |
| Cross-repository integration | C1 consumer adoption, real member/Store/reveal/canonical codec/renderer | Deferred to consumer integration stage; not an SDK-only claim |

Initial actual invocation test failed on JWE+Paper due to the pre-existing singleton-JWE validator. That failed assumption is preserved in the change-order history; tests now assert the released contract rather than expanding native/shared validation. The caller context guard establishes agreement, not server authentication or payload admission. No provider operations were executed.

Snapshot correction: TestSecretOutputAdvertisedSchemaIsAnImmutableSnapshot failed in all six global/resource × absent/generic/password_v2 cases before the fix. The final full race run passes original-pointer, listing-copy and getter-copy mutation controls while actual invocation agrees with the unchanged advertised declaration. Proto inputs/generated files are unchanged by this correction; no regeneration was run.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Field metadata transition

`config.Field.content_type` is MIME metadata. Logical delivery types use one
`multipass-type-` tag, for example `multipass-type-api_key_v2`. Other tags do
not select a type. The earlier prototype used Field 8 for a bare logical type;
that encoding is incompatible and valid bare values are rejected. JSON using
`secret_content_type` on a Field is also rejected by the legacy transport.
Native `request_schema` and descriptor `secret_content_type` remain readable;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Suggestion: This says native request_schema and descriptor secret_content_type "remain readable", and that new and legacy declarations must agree. Neither surface exists in this PR. A reader would expect a compatibility path that doesn't exist, so please remove the claim or move it to the PR that adds those surfaces (#1195).

when new and legacy declarations coexist they must agree. An absent return
field preserves the legacy issuance contract. A declared native return is one
named required secret string whose name must match the plaintext result.

Request values remain nonsecret. Hosts apply defaults before approval; SDK
validation does not apply defaults. Payload bytes are opaque and are not
validated against MIME or logical type metadata.
48 changes: 48 additions & 0 deletions docs/verification/connector-secret-types/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Connector-declared secret action output types

## Frozen behavior

A connector declares the content type of each secret action output before its handler runs. The connector owns the value bytes and codec. The caller chooses the destination and acknowledges the declared content type before producer side effects. One secret output can contain a document with multiple fields.

An absent declaration preserves existing generic output behavior. A present empty declaration, declarations on inputs or public outputs, and malformed identifiers are invalid. Existing arbitrary encryption context behavior remains compatible for undeclared/generic outputs. Explicit structured outputs require a matching typed destination acknowledgement before action allocation and handler execution; an older caller that supplies generic context cannot silently reinterpret a structured output.

The action schema at registration and invocation is an immutable snapshot. Later caller mutations of schema pointers or encryption configurations cannot change an invocation's declaration or authenticated context.

Secret bytes remain separate from public return fields and are encrypted without conversion. This change introduces no credential issuance protocol, typed input support, provider lifecycle, release or activation.

## Risk and verification

HIGH: a mixed-version caller can otherwise silently change durable payload meaning after a producer performs an external effect. Prevention must run before the handler; post-result checking cannot reverse that effect. No grant-scale or artifact-open hot path changes: checks are linear in schema fields and encryption configurations, with a bounded context parse only for explicit typed outputs.

| Criterion | Required instrument | Initial status |
| --- | --- | --- |
| Declaration absent/present round-trip | Descriptor and binary/JSON schema round-trip | Evidence incomplete |
| Invalid placement/value refused | Global/resource registration tables; assert no schema published | Evidence incomplete |
| Old generic caller refuses typed output | Invoke actual ActionManager with generic/missing/wrong context; producer-call count zero | Evidence incomplete |
| Matching typed context reaches producer | Actual manager, real JWE recipient and encrypted structured bytes | Evidence incomplete |
| Legacy generic compatibility | Existing secret-action tests plus arbitrary-context controls | Evidence incomplete |
| Immutable declaration/config | Mutate original schema/config after registration/dispatch; verify frozen output | Evidence incomplete |
| Result boundaries | Public-secret collision, undeclared/duplicate/required result checks | Existing tests; current change evidence incomplete |
| Generated compatibility | Owning protobuf generation and diff, source committed before generated | Evidence incomplete |

Instrument adequacy: demonstrate the mixed-version regression fails when its pre-handler acknowledgement check is removed. Review changed branch coverage and preserve explicit gaps. Registration global/resource paths are a required cross-product; ordinary/encrypted handler registration both enforce declaration placement. No test contacts an upstream provider.

## Boundary resolution

Exact additive field and acknowledgement interpretation require architecture disposition before schema or authority changes. Source inspection was explicitly required to establish whether existing APIs suffice; it preceded this plan. Preserve that exception instead of claiming implementation-blind discovery.

## Change orders

Append any contract or enforcement ownership change here before implementation.

### Change order 1: resolved declaration and caller acknowledgement

Architecture disposition selects optional config.Field.secret_content_type=8 and a pre-handler check of existing full-knowledge JWE context purpose/version/type. Check every JWE recipient, reject duplicate JSON keys, and require one secret return for non-generic types. A matching JWE recipient may coexist with Paper/age recipients. Generic declarations retain opaque contexts and existing multi-output transport. The guard establishes type agreement only, not server authentication or payload validity. Tests cover global/resource invocation, invalid placement, mixed recipients and immutable registered type metadata. Full catalog ownership remains with the consumer; SDK checks syntax only.

### Change order 2: preserve existing singleton-JWE transport

Executed invocation evidence showed that released ValidateEncryptionConfigs rejects any JWE recipient in a configuration set with length other than one. The earlier JWE+Paper positive assumption was counterfactual. Architecture correction preserves that existing restriction; JWE+age and multi-JWE are rejection controls before action allocation/handler effects. The positive typed path uses exactly one JWE recipient and one secret output. Generic age-only multi-recipient/multi-output behavior is unchanged. NewEncryptionManager does not itself enforce this configuration validation; the common ActionManager invocation boundary does, before construction and handler dispatch. No shared/native crypto API changes are introduced.

### Change order 3: immutable advertised schema

A source check identified a correction required by the original immutable-registration criterion: the guard copied type strings, but registration and schema getters retained shared mutable schema pointers. The new six-case global/resource regression failed when caller mutation changed the advertised absent/generic/password declaration into api_key_v2 while the registered handler retained its original type. Publication and read APIs now clone schemas. Mutations of the original, listed and fetched schemas cannot alter advertised/enforced agreement. This adds linear copying in action schema size on registration/discovery, outside declared artifact-open/grant-scale hot paths; no provider, store or credential lifecycle changes.
39 changes: 36 additions & 3 deletions pb/c1/config/v1/config.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

83 changes: 83 additions & 0 deletions pb/c1/config/v1/config.pb.validate.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading