Skip to content
This repository was archived by the owner on Aug 4, 2026. It is now read-only.

feat: make coupling runtime seed-only - #2

Merged
Yeusepe merged 3 commits into
mainfrom
feat/seeded-coupling-attestation
Jun 26, 2026
Merged

Yeusepe merged 3 commits into
mainfrom
feat/seeded-coupling-attestation

Conversation

@Yeusepe

@Yeusepe Yeusepe commented Jun 26, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Remove blind and legacy text decode paths from the TypeScript FFI and native exports.
  • Require seed-only v2 decoding for PNG and FBX attribution.
  • Add seed-iteration attribution coverage and refresh the tracked Windows runtime artifacts.

Verification

  • bun run typecheck
  • bun test

Summary by CodeRabbit

  • New Features
    • Added a coupling attribution workflow (/v1/coupling/attribute) that iterates seeded candidates per asset and reports matched results.
  • Bug Fixes
    • Standardized coupling scan/decode to seeded PNG/FBX only, including stricter request validation and clearer decode-failure reporting.
    • Tightened token/seed validation to v2 expectations (including exact token hex length); updated server responses to advertise supported assetTypes as PNG/FBX.
  • Tests
    • Updated end-to-end and unit tests to reflect seeded v2 behavior and the new token-length contract.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Yeusepe, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 21 minutes and 45 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1a3dd98d-2b92-40a7-a025-5dcb08507a23

📥 Commits

Reviewing files that changed from the base of the PR and between 8ffc046 and 3cec78f.

📒 Files selected for processing (1)
  • src/roundtrip.test.ts
📝 Walkthrough

Walkthrough

The PR narrows coupling handling to seeded v2 PNG/FBX paths, adds a coupling attribution endpoint, updates scan and materialization tests for required seeds, and removes legacy native exports and implementations.

Changes

Seeded v2 coupling and attribution

Layer / File(s) Summary
Public coupling contract
src/ffi.ts, src/couplingSeed.test.ts, src/ffi.test.ts, src/materialize.test.ts
CouplingAssetType is narrowed to png/fbx, CouplingDecodeInput requires seedHex, legacy decode constants are removed, the native symbol map drops legacy decoder bindings, and the related seed and token-width tests are updated.
Scan normalization and task building
src/server.ts, src/roundtrip.test.ts
Scan task validation now requires seeded PNG/FBX inputs, updates error and asset-type normalization, builds multipart and JSON tasks with required seedHex, reports png/fbx in health metadata, and the roundtrip tests cover required-seed decode behavior and validation cases.
Attribution endpoint
src/server.ts, src/roundtrip.test.ts
POST /v1/coupling/attribute validates candidates and assets, writes temporary files, derives per-candidate seeds, decodes assets, hashes decoded tokens, and returns per-asset match results; the roundtrip test covers matched and unmatched cases.
Legacy native runtime removal
yucp_coupling/coupling_runtime.c, yucp_coupling/guard.c
The runtime-helper export comment and cleanup now describe seeded v2-only wrappers, and legacy PNG/text and FBX helper implementations are removed from guard.c.
Export tables and checksums
src/nativeExports.test.ts, yucp_coupling/*.def, yucp_coupling/out/win-x64/Release/*.sha256
The .def export lists now expose only the seeded v2 symbols with new ordinals, the recorded SHA-256 files are updated, and the export-ordinal tests assert the remaining symbols.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~90+ minutes

Poem

🐰 I hopped on seeds through PNG and FBX light,
Sniffed out old exports and tucked them out of sight.
New hashes twinkled, crisp and neat,
While v2 carrots made the token trail complete.
Hoppity! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: coupling runtime is being converted to seed-only behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

Remove the blind and legacy text decode surface from the TypeScript FFI and native export table so attribution always requires the per-job seed that placed the mark.

Add the seed-iteration attribution route coverage and update the tracked Windows runtime artifacts to match the reduced v2 PNG/FBX native surface.

Verification: bun run typecheck; bun test.
@Yeusepe
Yeusepe force-pushed the feat/seeded-coupling-attestation branch from 79c8f47 to 089fd5b Compare June 26, 2026 21:21
@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

#define WM_TOKEN_HEX 16

P1 Badge Align token length validation with native v2

When an existing caller or control-plane coupling job sends a tokenHex length that normalizeTokenHexForFfi still accepts (8-64 hex, for example the previous 32-hex FBX default), this new WM_TOKEN_HEX = 16 contract makes both native encoders reject it with -3. That bubbles out of encodeCouplingAsset/materialization as a native failure instead of a request validation error, so jobs using previously valid token lengths will fail unless the TypeScript/control-plane contract is tightened to exactly 16 hex or native continues supporting variable lengths.


process.env.COUPLING_WM_MASTER_KEY =
'00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff';

P2 Badge Set the seed on cached config in tests

When another test imports ./config before this file runs (for example bun test src/ffi.test.ts src/couplingSeed.test.ts), config.wmMasterKeyHex has already been captured from the environment, so assigning process.env.COUPLING_WM_MASTER_KEY here is too late and every seed derivation assertion sees the master key as unconfigured. Update the cached config object or isolate this module import so the test suite does not depend on file execution order.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@coderabbitai coderabbitai 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.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/ffi.ts (1)

217-229: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject legacy-width tokens before calling the v2-only encoder.

This block now sends every token through xg_0122/xg_0124, but the JS-side validator still accepts 8-64 hex chars. The updated materialization fixture had to drop from 32 to 16 chars for seeded v2 embedding, so longer legacy tokens can now cross the FFI boundary and fail only in native code. Tighten the TypeScript contract to the v2 token width before dispatching here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ffi.ts` around lines 217 - 229, Reject legacy-width tokens in
encodeCouplingAsset before dispatching to the v2 native encoders xg_0122 and
xg_0124. Tighten the TypeScript-side validation contract for
CouplingEncodeInput.tokenHex so only the v2 token width is accepted, using the
existing normalization helpers normalizeTokenHexForFfi and
normalizeSeedHexForFfi to enforce the narrower length before building the native
pointers and calling loadNativeLibrary().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/server.ts`:
- Around line 850-856: Reject oversized candidate sets in the attribute matching
flow instead of truncating them: in the server-side handling around the
candidates cap check and the AttributeCandidate loop, replace the
warning-and-slice behavior with an explicit error response when body.candidates
exceeds MAX_ATTRIBUTE_CANDIDATES. Make the change in the logic that builds the
candidates array so oversized inputs fail fast and do not continue into the
matching path with partial data.
- Around line 947-964: Handle failures in the candidate loop in server.ts
without swallowing service errors: in the logic around deriveCouplingSeedHex and
decodeCouplingAsset, only treat expected “no match” cases as misses, but let
invalid master-key/configuration errors and unexpected decode/runtime exceptions
propagate so the request fails instead of returning matched: false. Use the
existing candidate-matching flow in the block that sets hit and breaks to
separate recoverable per-candidate mismatches from real service failures.
- Around line 917-922: The asset upload validation in the `contentBase64`
handling is too weak because `Buffer.from(..., 'base64')` can accept malformed
input without throwing. Update the `src/server.ts` asset decoding flow around
the `Uint8Array.from(Buffer.from(...))` block to perform strict base64
validation before decoding, and keep the `HttpError` path for invalid
`contentBase64` in the asset write logic.

In `@yucp_coupling/yucp_coupling.def`:
- Around line 3-6: The export table is being renumbered in yucp_coupling.def,
which breaks ordinal-based Windows consumers. Keep the surviving exports
xg_0122, xg_0123, xg_0124, and xg_0125 at their original ordinals instead of
compressing them to `@1-`@4, and leave gaps where the removed legacy exports used
to be. Apply the same ordinal-preserving layout in
yucp_coupling.runtime-helper.def so both DEF files stay aligned.

---

Outside diff comments:
In `@src/ffi.ts`:
- Around line 217-229: Reject legacy-width tokens in encodeCouplingAsset before
dispatching to the v2 native encoders xg_0122 and xg_0124. Tighten the
TypeScript-side validation contract for CouplingEncodeInput.tokenHex so only the
v2 token width is accepted, using the existing normalization helpers
normalizeTokenHexForFfi and normalizeSeedHexForFfi to enforce the narrower
length before building the native pointers and calling loadNativeLibrary().
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c018b9d3-7809-41c8-b409-b3f4a21a3dde

📥 Commits

Reviewing files that changed from the base of the PR and between 23836c9 and 089fd5b.

⛔ Files ignored due to path filters (4)
  • yucp_coupling/out/win-x64/Release/coupling_runtime.obj is excluded by !**/*.obj
  • yucp_coupling/out/win-x64/Release/runtime-helper/coupling_runtime.obj is excluded by !**/*.obj
  • yucp_coupling/out/win-x64/Release/runtime-helper/yucp_coupling.dll is excluded by !**/*.dll
  • yucp_coupling/out/win-x64/Release/yucp_coupling.dll is excluded by !**/*.dll
📒 Files selected for processing (16)
  • coupling_runtime.exp
  • coupling_runtime.lib
  • src/ffi.ts
  • src/materialize.test.ts
  • src/roundtrip.test.ts
  • src/server.ts
  • yucp_coupling/coupling_runtime.c
  • yucp_coupling/guard.c
  • yucp_coupling/out/win-x64/Release/runtime-helper/yucp_coupling.compile.pdb
  • yucp_coupling/out/win-x64/Release/runtime-helper/yucp_coupling.pdb
  • yucp_coupling/out/win-x64/Release/runtime-helper/yucp_coupling.sha256
  • yucp_coupling/out/win-x64/Release/yucp_coupling.compile.pdb
  • yucp_coupling/out/win-x64/Release/yucp_coupling.pdb
  • yucp_coupling/out/win-x64/Release/yucp_coupling.sha256
  • yucp_coupling/yucp_coupling.def
  • yucp_coupling/yucp_coupling.runtime-helper.def
💤 Files with no reviewable changes (1)
  • yucp_coupling/guard.c

Comment thread src/server.ts Outdated
Comment thread src/server.ts Outdated
Comment thread src/server.ts Outdated
Comment thread yucp_coupling/yucp_coupling.def Outdated
Comment on lines +3 to +6
xg_0122 @1
xg_0123 @2
xg_0124 @3
xg_0125 @4

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve the old ordinals when shrinking the export table.

Reassigning the surviving symbols to @1-@4 breaks any Windows consumer or import library that binds by ordinal. Keep xg_0122-xg_0125 at their previous ordinals and leave gaps for the removed legacy exports; yucp_coupling.runtime-helper.def should mirror that approach.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@yucp_coupling/yucp_coupling.def` around lines 3 - 6, The export table is
being renumbered in yucp_coupling.def, which breaks ordinal-based Windows
consumers. Keep the surviving exports xg_0122, xg_0123, xg_0124, and xg_0125 at
their original ordinals instead of compressing them to `@1-`@4, and leave gaps
where the removed legacy exports used to be. Apply the same ordinal-preserving
layout in yucp_coupling.runtime-helper.def so both DEF files stay aligned.

Reject oversized attribution candidate sets instead of truncating them, validate base64 input strictly, and let non-native service failures abort the request.

Align TypeScript token validation with the native v2 16-hex contract and preserve Windows export ordinals for the surviving seeded symbols.

Rebuild the Windows native artifacts and add regressions for the review findings.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/roundtrip.test.ts`:
- Around line 169-170: The health-check failure path is incorrectly awaiting the
live stderr stream via new Response(service.stderr).text(), which can block
until EOF and hang the test helper; update the timeout handling in the roundtrip
test helper to avoid waiting on the pipe after the service fails to become
healthy, and instead use only non-blocking, already-available error information
from the service/stderr capture when constructing the thrown error message.
- Around line 639-644: The test setup in the asset payload is using an obviously
invalid PNG blob, which makes the failure depend on asset decoding instead of
the intended bad seed path. Update the asset fixture in roundtrip.test.ts to use
a tiny valid PNG payload in the assets array so the 500 internal_error assertion
continues to target the misconfigured COUPLING_WM_MASTER_KEY flow. Keep the
change localized to the leak.png fixture used by this test case.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 622ba45f-215c-4776-b9c5-797c31b69512

📥 Commits

Reviewing files that changed from the base of the PR and between 089fd5b and 8ffc046.

⛔ Files ignored due to path filters (4)
  • yucp_coupling/out/win-x64/Release/coupling_runtime.obj is excluded by !**/*.obj
  • yucp_coupling/out/win-x64/Release/runtime-helper/coupling_runtime.obj is excluded by !**/*.obj
  • yucp_coupling/out/win-x64/Release/runtime-helper/yucp_coupling.dll is excluded by !**/*.dll
  • yucp_coupling/out/win-x64/Release/yucp_coupling.dll is excluded by !**/*.dll
📒 Files selected for processing (16)
  • coupling_runtime.exp
  • coupling_runtime.lib
  • src/couplingSeed.test.ts
  • src/ffi.test.ts
  • src/ffi.ts
  • src/nativeExports.test.ts
  • src/roundtrip.test.ts
  • src/server.ts
  • yucp_coupling/out/win-x64/Release/runtime-helper/yucp_coupling.compile.pdb
  • yucp_coupling/out/win-x64/Release/runtime-helper/yucp_coupling.pdb
  • yucp_coupling/out/win-x64/Release/runtime-helper/yucp_coupling.sha256
  • yucp_coupling/out/win-x64/Release/yucp_coupling.compile.pdb
  • yucp_coupling/out/win-x64/Release/yucp_coupling.pdb
  • yucp_coupling/out/win-x64/Release/yucp_coupling.sha256
  • yucp_coupling/yucp_coupling.def
  • yucp_coupling/yucp_coupling.runtime-helper.def
✅ Files skipped from review due to trivial changes (2)
  • yucp_coupling/out/win-x64/Release/yucp_coupling.sha256
  • yucp_coupling/out/win-x64/Release/runtime-helper/yucp_coupling.sha256
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/ffi.ts
  • src/server.ts

Comment thread src/roundtrip.test.ts Outdated
Comment thread src/roundtrip.test.ts

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ffc046fd0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 15 to 16
#define xg_0122 yucp_runtime_helper_encode_seeded_png
#define xg_0124 yucp_runtime_helper_encode_seeded_fbx

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Rebuild Linux runtime artifacts without legacy exports

This source change removes the runtime-helper legacy wrappers, but the committed Linux runtime-helper artifact was not refreshed: nm -D --defined-only yucp_coupling/out/linux-x64/Release/runtime-helper/yucp_coupling.so still shows xg_0115 and xg_0120 alongside the seeded exports, and the server .so still exports the removed legacy decoders. On Linux clients/deployments this means the shipped binary still contains the pre-seed encoder path even though the TypeScript and Windows artifacts are seed-only, undermining the security goal of this change; please rebuild/update the Linux artifacts and hashes as part of this commit.

Useful? React with 👍 / 👎.

Comment thread src/server.ts
const candidates: AttributeCandidate[] = [];
for (const raw of body.candidates) {
const c = raw as { assetPath?: unknown; licenseSubject?: unknown; tokenHash?: unknown };
const assetPath = typeof c.assetPath === 'string' ? c.assetPath.trim() : '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize candidate paths before deriving attribution seeds

For attribution candidates coming from Windows manifests or from the same raw paths accepted by /v1/coupling/internal/derive-seeds, this only trims the candidate path before it is later passed to deriveCouplingSeedHex. The seed endpoint normalizes backslashes and leading slashes before HKDF, so a candidate like \Assets\Test\leak.png derives a different seed here than it did when the watermark was placed, causing a real buyer to be reported as unmatched; normalize and validate candidate paths the same way as seed derivation before storing them.

Useful? React with 👍 / 👎.

Capture service stderr asynchronously so health-check timeouts never block on a live stream.

Use a valid PNG fixture in the bad seed configuration regression so it tests the intended failure path.
@Yeusepe

Yeusepe commented Jun 26, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3cec78fe70

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 15 to 16
#define xg_0122 yucp_runtime_helper_encode_seeded_png
#define xg_0124 yucp_runtime_helper_encode_seeded_fbx

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Rebuild Linux artifacts after removing legacy exports

After dropping the helper legacy exports here, the committed Linux artifacts were not rebuilt: I checked nm -D yucp_coupling/out/linux-x64/Release/runtime-helper/yucp_coupling.so and it still exports xg_0115/xg_0120, while the server .so still exports the legacy decoders xg_0118/xg_0119/xg_0121. On Linux hosts config.dllPath defaults to out/linux-x64/Release/yucp_coupling.so, so Linux deployments still ship the removed legacy/blind runtime even though the TypeScript layer and Windows DLLs are seed-only; rebuild and commit the Linux .so and hash artifacts from this source change.

Useful? React with 👍 / 👎.

Comment thread src/server.ts
const candidates: AttributeCandidate[] = [];
for (const raw of body.candidates) {
const c = raw as { assetPath?: unknown; licenseSubject?: unknown; tokenHash?: unknown };
const assetPath = typeof c.assetPath === 'string' ? c.assetPath.trim() : '';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Normalize candidate paths before deriving seeds

When attribution candidates come from paths with Windows separators or a leading slash, this raw trim() value is fed into deriveCouplingSeedHex, while seed creation elsewhere normalizes the same asset path via normalizeRelativeAssetPath/assertSafeProjectRelativePath. In that case a candidate for \Assets\Test\leak.png derives a different seed than the one used to embed Assets/Test/leak.png, so /v1/coupling/attribute returns matched: false even with the correct buyer and token hash; normalize and validate candidate paths before storing them.

Useful? React with 👍 / 👎.

@Yeusepe
Yeusepe merged commit 2beeaf2 into main Jun 26, 2026
1 check passed
@Yeusepe
Yeusepe deleted the feat/seeded-coupling-attestation branch June 26, 2026 22:02
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant