Skip to content

Select the credential envelope explicitly, and declare the provenance facts the binary knows - #6

Merged
ualtinok merged 2 commits into
cortexkit:masterfrom
iceteaSA:fix/vault-envelope-precedence
Aug 30, 2026
Merged

Select the credential envelope explicitly, and declare the provenance facts the binary knows#6
ualtinok merged 2 commits into
cortexkit:masterfrom
iceteaSA:fix/vault-envelope-precedence

Conversation

@iceteaSA

@iceteaSA iceteaSA commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Opened at your invitation on #3. Two commits, separable — take either alone.

1. fix(vault): envelope selection by decision, not by variant order — closes residual 3 of #3

CredentialGetResponse was #[serde(untagged)] with Direct declared before Wrapped. Untagged enums are tried in declaration order, so a frame carrying both a wrapped result.error and top-level success fields decoded as a SUCCESS and discarded the error — fetch() would return a CredentialToken built from the stray bytes and hand it to a provider call.

result now wins whenever present. The unwrapped form stays accepted, as a documented fallback rather than a coincidence of ordering.

Scope, stated honestly: claustrum cannot emit that frame today. Their GetOutcome is an enum whose variants cannot coexist, confirmed from their types, so this is a latent hazard rather than a live bug. What made it worth fixing is the direction it fails in — toward serving a credential — and that the envelope choice rested on declaration order rather than on a decision.

Also pins the missing-code frame. It was already rejected, but only because code is non-Option — an accident of the type rather than an intention. Adding #[serde(default)] later now reddens a test instead of silently widening what synapse accepts from the vault.

Three tests: the defect, the fallback, and the missing-code frame.

2. feat(manifest): declare provenance facts instead of blanket-None

provenance: None carried a comment about release scripts not stamping CK_BUILD_* yet. That reasoning holds for the build facts and not for the other two:

  • wire_crate_version is a compile-time constant of the linked subc-protocol crate.
  • The newest migration this binary carries is a fact a daemon can compare against a store's actual version to spot a stale binary directly.

Uses the SDK helper, which stamps wire_crate_version from the linked crate and maps an absent build fact to field omission rather than to a sentinel string. Build facts stay absent.

newest_schema_version() derives from the migration list rather than restating it — a literal keeps reporting the old number the first time a migration is appended, which is the drift this field exists to catch. It uses max rather than last so it does not rest on the list happening to stay sorted.

The test deliberately does not restate the schema number. Asserting a derived value against the same derivation would make the test agree with the code by construction and pass whatever the migration list said.

Mutation proof

Each test was verified capable of failing, with a positive control asserting the mutation target existed exactly once before editing — a substitution that silently matches nothing is indistinguishable from a test that catches nothing.

mutation result
envelope precedence reverted to Direct-before-Wrapped FAIL wrapped_error_wins_over_stray_top_level_success_fieldsstray top-level success fields must not outrank a wrapped error; the other four vault tests stayed green
provenance reverted to None FAIL manifest_provenance_declares_only_facts_this_binary_knowsan SDK module always has at least one honest provenance fact
schema fact dropped (build_provenance(None, None, None)) FAILa module with a migration list can state its newest migration
wire_crate_version set to synapse's own crate version FAILleft: Some("0.0.0"), right: Some("0.14.0")

The vault mutation left the other four tests green, so that test discriminates rather than merely triggering. Every mutation was reverted and the file confirmed byte-identical to its pre-mutation copy.

One control fired for real: a second mutation stacked on an unreverted first was refused by its own positive control (target not found exactly once (count=0)) instead of running and reporting a meaningless pass.

Gate

Run at the pushed sha adfa363 — a gate result is a claim about a tree, not about a diff — inside a network namespace with egress dead by DNS name and raw IP, loopback positive control live before and after:

577 tests run: 577 passed, 3 skipped
skip census: 15                       (unchanged — no new silent skipping)
clippy --all-targets: clean
cargo fmt --all --check: clean
Cargo.lock: untouched

Scope caveat: 4 crates (synapse-core, synapse-engine-ort, synapse-module, owned-decode-worker). CI runs 6 — synapse-worker-llama pulls a llama.cpp C++ build and synapse-worker-decode is macOS-gated, neither reachable here.

Build note for reviewers: master now requires subc-protocol 0.14.0 for self_signals. This was built against an isolated sibling checkout at that version rather than by moving the shared fleet tree; Cargo.lock is untouched, which is the check that the sibling set was right rather than silently downgrading.

Not included

_ => PauseJob is unchanged — contract-backed per the resolution on #3, and claustrum has since published the widening rule that makes it correct.

Residuals 1 and 2 of #3 are untouched. They are judgement calls about your error vocabulary rather than mechanical fixes. Residual 1 did stop being hypothetical, though: claustrum deployed 706e257 today, so credential.get on a signing-key handle now returns kind_not_gettable — a vault-side wrong-verb condition that synapse still reports to consumers as credential_config_invalid.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.


Summary by cubic

Fixes vault credential envelope selection so a wrapped error can't be misread as a credential success, and declares the provenance facts synapse actually knows instead of blanket-None.

Bug Fixes

  • Selects the envelope by the result key's presence rather than untagged variant order; claustrum can't emit the mixed frame today, but the old code failed toward serving a credential.
  • Pins a missing code on a credential error as malformed; it was rejected by the type, not by intent.

New Features

  • Declares wire_crate_version from the linked subc-protocol crate and the newest migration version; build facts stay absent.
  • newest_schema_version() derives from the migration list via max so appending a migration can't stale the reported number.

Written for commit adfa363. Summary will update on new commits.

Review in cubic

… order

CredentialGetResponse was #[serde(untagged)] with Direct declared before
Wrapped, so a frame carrying both a wrapped result.error and top-level
success fields decoded as a SUCCESS and discarded the error — fetch()
would return a CredentialToken built from the stray bytes and hand it to
a provider call. Claustrum cannot emit that frame today (their outcome is
an enum whose variants cannot coexist), but the envelope choice rested on
declaration order rather than on a decision, and it failed toward serving
a credential.

result now wins whenever present; the unwrapped form stays accepted as a
documented fallback rather than a coincidence of ordering.

Also pins the missing-code frame, which was rejected only because code is
non-Option — an accident of the type rather than an intention. Adding
#[serde(default)] later now reddens a test instead of silently widening
what synapse accepts from the vault.

Refs cortexkit#3
provenance was blanket-None with a comment about release scripts not
stamping CK_BUILD_* yet. That reasoning holds for the build facts and not
for the other two: wire_crate_version is a compile-time constant of the
linked subc-protocol crate, and the newest migration this binary carries
is a fact a daemon can compare against a store's actual version to spot a
stale binary directly. Blanket-None where a field is knowable wastes the
field.

Uses the SDK helper, which stamps wire_crate_version from the linked
crate and maps an absent build fact to field omission rather than to a
sentinel string -- a present, well-formed value stops the reader asking.

newest_schema_version derives from the migration list rather than
restating it: a literal keeps reporting the old number the first time a
migration is appended, which is the drift this field exists to catch.

The test deliberately does not restate the schema number. Asserting a
derived value against the same derivation would make the test agree with
the code by construction and pass whatever the migration list said.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="crates/synapse-module/src/lib.rs">

<violation number="1" location="crates/synapse-module/src/lib.rs:14046">
P3: The `store_schema_version` assertion only checks that the value parses to a number greater than 0, so it can never catch a regressed derivation that still yields a positive value. With the current single-migration list (version 1), even changing `newest_schema_version()` to `.min()`, `.last()`, or a hardcoded `1` keeps the asserted bound true and this test passes, which contradicts the PR's mutation-testing note that derivation mutations around schema versioning are detectable. This is the only assertion that exercises `newest_schema_version()`, so a broken derivation is effectively unobserved.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

assert!(
schema_version
.parse::<u32>()
.is_ok_and(|version| version > 0),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: The store_schema_version assertion only checks that the value parses to a number greater than 0, so it can never catch a regressed derivation that still yields a positive value. With the current single-migration list (version 1), even changing newest_schema_version() to .min(), .last(), or a hardcoded 1 keeps the asserted bound true and this test passes, which contradicts the PR's mutation-testing note that derivation mutations around schema versioning are detectable. This is the only assertion that exercises newest_schema_version(), so a broken derivation is effectively unobserved.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At crates/synapse-module/src/lib.rs, line 14046:

<comment>The `store_schema_version` assertion only checks that the value parses to a number greater than 0, so it can never catch a regressed derivation that still yields a positive value. With the current single-migration list (version 1), even changing `newest_schema_version()` to `.min()`, `.last()`, or a hardcoded `1` keeps the asserted bound true and this test passes, which contradicts the PR's mutation-testing note that derivation mutations around schema versioning are detectable. This is the only assertion that exercises `newest_schema_version()`, so a broken derivation is effectively unobserved.</comment>

<file context>
@@ -14002,6 +14010,48 @@ mod tests {
+        assert!(
+            schema_version
+                .parse::<u32>()
+                .is_ok_and(|version| version > 0),
+            "store_schema_version must be a real migration number, got {schema_version:?}"
+        );
</file context>

@ualtinok
ualtinok merged commit adfa363 into cortexkit:master Aug 30, 2026
4 checks passed
@synapse-alfonso

Copy link
Copy Markdown

Merged, both commits. Review notes: I mutation-verified the envelope precedence test independently (reintroduced a top-level-first selection; your test failed naming the right invariant) and ran the full battery + clippy + fmt + banlist on the branch — 386 green. The provenance commit is the part I'd single out: declaring wire_crate_version and the derived newest-migration number while leaving build facts absent is the honest-declaration doctrine applied better than our own blanket None was, and the test's refusal to restate the derived value is the right self-discipline. The missing-code pin (type-accident promoted to guarantee) closes residual 1 properly. Thanks — this sets the bar for external contributions here.

ualtinok pushed a commit that referenced this pull request Aug 30, 2026
The manifest-side assertion only checks that store_schema_version parses
to a positive number, so a regressed derivation that still returns a
positive value is invisible there. Verified by mutation: changing
newest_schema_version to .min() left all 22 tests in the area green.

The fix is not to restate the derivation in the test -- that would make
the test agree with the code by construction and pass whatever the
function did. It asserts the two bounds the result must satisfy: no
migration exceeds it, and it is a version the list actually contains.

.last() stays green while the list is sorted, because it then returns the
same value as .max(). That is an equivalent mutant rather than a gap, and
the first bound catches it as soon as the list is unsorted -- verified,
not asserted, by reversing the iteration order.

Reported by cubic on #6. The finding was right; its stated reason was not
-- the list holds 12 migrations, not one, and .min() survives because 1
is still greater than 0.
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.

2 participants