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

feat: add attestation identity relay - #37

Merged
Yeusepe merged 10 commits into
mainfrom
feat/attestation-identity-relay
Jun 25, 2026
Merged

Yeusepe merged 10 commits into
mainfrom
feat/attestation-identity-relay

Conversation

@Yeusepe

@Yeusepe Yeusepe commented Jun 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Add Convex attestation storage and internal relay routes for challenge issuance, attestation resolution, coupling proofs, payment anchors, and blocked-identity unlock gating.
  • Wire attestation into the production regression surface and external integration gate.
  • Cover the seeded coupling job path with a closed-service seed relay regression.

Verification

  • bun audit
  • bun run lint
  • bun run typecheck
  • bun run test:convex
  • bun run test:external-integrations
  • bun run test:ci
  • bun x vitest run --config convex/vitest.config.ts convex/couplingJobAndReveal.realtest.ts convex/attestation.realtest.ts
  • bun test ops/production-regression-loop.test.ts

Summary by CodeRabbit

  • New Features
    • Added a hardware-attested anti-ripper identity system, including single-use TTL challenge handling, identity merging via durable anchors, payment-fingerprint linking, and an admin review workflow for blocking/unblocking.
    • Introduced internal attestation-relay HTTP endpoints for challenge, attestation recording, coupling proofs, identity blocking/review, and payment-anchor attachment.
    • Coupling-seed relay support now drives per-asset seeded tokens, improving determinism and eligibility checks.
  • Bug Fixes
    • Protected unlock issuance is now correctly blocked for actively blocked identities, and properly unblocked after reversals.
  • Tests
    • Expanded attestation real tests, HTTP endpoint hardening tests, coupling relay tests, and updated regression coverage to include the new attestation surface.

Add the Convex-side attestation store and internal relay routes for challenge issuance, opaque attestation resolution, coupling proofs, payment anchors, and blocked-identity unlock gating.

Wire attestation into the production regression surface and external integration gate, and cover the seeded coupling job path with a closed-service seed relay test.

Verified with bun audit, bun run lint, bun run typecheck, bun run test:convex, bun run test:external-integrations, bun run test:ci, targeted Convex attestation/coupling tests, and the production regression loop test.

Yeusepe commented Jun 25, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review
@codex review

@coderabbitai

coderabbitai Bot commented Jun 25, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a7d4a5b1-1ad3-46bb-8818-5af139344e27

📥 Commits

Reviewing files that changed from the base of the PR and between 29a57c5 and 861b530.

📒 Files selected for processing (3)
  • convex/attestation.realtest.ts
  • convex/attestation.ts
  • convex/protectedUnlock.realtest.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Workers Builds: creator-assistant-dashboard
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx,js,jsx}: Act as a strict TypeScript and JavaScript security auditor.
Aggressively flag:

  • SSRF, injection, XSS, prototype pollution, insecure redirects, unsafe dynamic imports, eval-like APIs, and shell command construction
  • API routes, server actions, RPC handlers, middleware, loaders, actions, edge functions, and webhooks without explicit authentication, authorization, CSRF protection where applicable, tenant isolation, and input validation
  • unsafe JSON parsing, schema gaps, mass assignment, confused-deputy issues, trusting client-controlled IDs or roles, and unsafe use of headers or cookies
  • tokens, secrets, private config, server-only env vars, API keys, tenant data, PII, or internal endpoints exposed to client bundles, hydration payloads, browser storage, source maps, analytics, logs, errors, or public assets
  • logging of request headers, cookies, tokens, user payloads, PII, tenant IDs, raw errors, or internal stack traces
  • non-robust workloads in Node runtimes, including missing abort signals, missing timeouts, unbounded promises, unbounded queues, unbounded response bodies, and event-loop blocking work

Files:

  • convex/protectedUnlock.realtest.ts
  • convex/attestation.realtest.ts
  • convex/attestation.ts
**/*test*

⚙️ CodeRabbit configuration file

**/*test*: Security still matters in tests.
Flag real-looking credentials, copied production tokens, unsafe fixtures with live endpoints, disabled TLS verification without isolation, frontend snapshots containing sensitive data, test logs that reveal secrets or PII, and tests that normalize insecure patterns.
Test-only bypasses must be isolated, obvious, and impossible to activate in production.

Files:

  • convex/protectedUnlock.realtest.ts
  • convex/attestation.realtest.ts
  • convex/attestation.ts
🔇 Additional comments (4)
convex/protectedUnlock.realtest.ts (2)

116-159: LGTM!


279-279: LGTM!

convex/attestation.ts (1)

5-6: LGTM!

Also applies to: 445-448, 462-463, 478-483

convex/attestation.realtest.ts (1)

64-75: LGTM!

Also applies to: 154-158, 175-179, 195-199, 225-229, 254-258, 292-328, 417-419, 454-454


📝 Walkthrough

Walkthrough

This PR adds attestation storage, identity review, HTTP relay endpoints, coupling-seed derivation, protected-unlock gating, and regression and realtest coverage.

Changes

Attestation and coupling flow

Layer / File(s) Summary
Schema and table model
convex/schema.ts
machine_attestations, identity_nodes, identity_node_anchors, blocked_identities, attestation_challenges, and coupling_proofs are added to the Convex schema with new attestation and identity types.
Identity resolution and review
convex/attestation.ts
Challenge issuance/consumption, identity-node resolution and merging, blocked-status lookup, payment-anchor attachment, coupling-proof storage, and manual block review are implemented in Convex.
HTTP relay routes
convex/http.ts
/v1/attestation/internal/challenge, /record, /coupling-record, /payment-anchor, /identity-blocks, and /identity-block-reviews validate opaque inputs, enforce CONVEX_API_SECRET, and call the internal attestation mutations.
Coupling seeds and unlock gating
convex/yucpLicenses.ts
Coupling-job seed derivation now includes per-asset seed hex values, and protected unlock checks the identity-block query before issuing access.
Attestation realtests
convex/attestation.realtest.ts
Realtests cover identity-node collapse, blocked inheritance, nonce replay, payment-anchor merges, coupling-proof linkage, and opaque-hash persistence.
Relay tests and regression wiring
convex/couplingJobAndReveal.realtest.ts, convex/httpSurface.behavior.test.ts, convex/protectedUnlock.realtest.ts, convex/protectedBlobArchitecture.realtest.ts, ops/production-regression-loop.ts, ops/production-regression-loop.test.ts
Coupling relay tests, HTTP surface tests, protected-unlock realtests, protected-blob realtests, and regression-loop coverage are updated for the attestation flow.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore (reviewers only)

❌ Failed checks (6 errors, 1 warning)

Check name Status Explanation Resolution
No Hardcoded Secrets ❌ Error Tests hardcode secret-like literals such as CONVEX_API_SECRET='test-secret', YUCP_ROOT_PRIVATE_KEY='root-private-key', and YUCP_COUPLING_SERVICE_SHARED_SECRET='coupling-secret'. Replace literal secret-like test values with generated dummy placeholders or load them from approved test secret fixtures; avoid hardcoded values for SECRET/TOKEN fields.
Reachable Endpoint Leakage ❌ Error /v1/licenses/unlock-protected returns a bearer unlockToken, and issueProtectedUnlock leaks account state with distinct blocked vs attestation errors. Avoid returning bearer tokens from reachable HTTP routes, or move issuance behind an internal-only channel; also collapse unlock failures to one generic 403.
Ssrf Egress Gate ❌ Error deriveCouplingSeeds fetches any https base URL from env with no host/IP allowlist or post-resolution checks; tests only reject non-loopback http. Restrict the relay to explicit trusted hosts (or loopback-only), validate resolved IPs against private/link-local/metadata ranges, keep redirect refusal, and add bypass tests for https localhost/IP/metadata/DNS rebinding.
Non Robust Workloads ❌ Error Hot-path attestation/merge code uses unbounded .collect() scans over growing tables (e.g. isIdentityBlocked, absorbNode), with no pagination or caps. Bound the scans with pagination/limits or store aggregate node/block state so unlock/merge paths never read entire growing tables.
Resource Exhaustion Dos ❌ Error New attestation POST routes use request.json() with no byte cap, and isIdentityBlocked/related lookups use uncapped .collect(), so large inputs can exhaust memory/CPU. Add a fixed request-size limit before JSON parsing, per-route rate limiting, and paginate or cap attestation queries instead of collecting entire result sets.
Security Regression Tests ❌ Error Attestation, relay, and unlock paths are tested, but there are no tests for 401 auth failures, nonce expiry, or oversized payload rejection on the new sensitive endpoints. Add regression tests for missing/invalid CONVEX_API_SECRET on each attestation route, challenge expiry, and oversized attestation/coupling payloads; note any impossible cases.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (14 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the PR’s main attestation identity relay and internal attestation route changes.
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.
Data Leakage Protection ✅ Passed Attestation routes only accept hashed/opaque inputs and return minimal allowlisted fields; tests assert no raw identifiers or secrets are exposed.
Frontend Client Bundle Leakage ✅ Passed Only backend/test/ops files changed; attestation routes are Bearer-protected internal Convex endpoints and no apps/web/public assets were modified.
Prompt Ai Data Leakage ✅ Passed No AI/model/prompt code was added; the only new egress is an allowlisted coupling-seed relay that sends opaque licenseSubject/assetPaths, not raw content.
Authentication Authorization Gate ✅ Passed New attestation routes all require Bearer CONVEX_API_SECRET, and protected unlock verifies signed license JWTs plus attestation/block state before issuing.
Injection Boundary Gate ✅ Passed New attestation/relay inputs are bounded, allowlisted, and regex-checked before fetch/DB use; internal ID sinks are revalidated by v.id.
Crypto Token Safety ✅ Passed Nonce/token generation uses crypto.getRandomValues/crypto.randomUUID, and JWT verification enforces EdDSA, expected iss/aud, exp, and iat.
Database Storage Safety ✅ Passed No destructive schema/migration ops found; new attestation storage is additive, opaque-hash-only, admin-bearer protected, and covered by tests.
General Vulnerability Gate ✅ Passed All new attestation/internal routes require CONVEX_API_SECRET and strict hash/length validation; protected unlock hashes the machine and checks attestation before issuing.
Dependency Runtime Cve Gate ✅ Passed Changed files are Convex app code/tests and ops config; no package manifests, lockfiles, Dockerfiles, or workflow deps were modified.
Iac Deployment Hardening ✅ Passed Touched files are Convex app/tests only; new attestation admin routes require CONVEX_API_SECRET, and relay egress forbids creds and HTTP except loopback.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/attestation-identity-relay

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

@coderabbitai

coderabbitai Bot commented Jun 25, 2026 •

Copy link
Copy Markdown

@Yeusepe Reviewing the changes now.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 25, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
creator-assistant-dashboard 861b530 Commit Preview URL

Branch Preview URL
Jun 25 2026, 06:58 PM

@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: 40ada9ca79

ℹ️ 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 thread convex/attestation.ts Outdated
Comment thread convex/attestation.ts
Comment thread convex/attestation.ts Outdated

@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: 9


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 48d9fb50-81d8-404b-81f8-290c7cdf86d1

📥 Commits

Reviewing files that changed from the base of the PR and between 49a2ed9 and 40ada9c.

⛔ Files ignored due to path filters (1)
  • convex/_generated/api.d.ts is excluded by !**/_generated/**
📒 Files selected for processing (9)
  • convex/attestation.realtest.ts
  • convex/attestation.ts
  • convex/couplingJobAndReveal.realtest.ts
  • convex/http.ts
  • convex/schema.ts
  • convex/tsconfig.tsbuildinfo
  • convex/yucpLicenses.ts
  • ops/production-regression-loop.test.ts
  • ops/production-regression-loop.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Workers Builds: creator-assistant-dashboard
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx,js,jsx}: Act as a strict TypeScript and JavaScript security auditor.
Aggressively flag:

  • SSRF, injection, XSS, prototype pollution, insecure redirects, unsafe dynamic imports, eval-like APIs, and shell command construction
  • API routes, server actions, RPC handlers, middleware, loaders, actions, edge functions, and webhooks without explicit authentication, authorization, CSRF protection where applicable, tenant isolation, and input validation
  • unsafe JSON parsing, schema gaps, mass assignment, confused-deputy issues, trusting client-controlled IDs or roles, and unsafe use of headers or cookies
  • tokens, secrets, private config, server-only env vars, API keys, tenant data, PII, or internal endpoints exposed to client bundles, hydration payloads, browser storage, source maps, analytics, logs, errors, or public assets
  • logging of request headers, cookies, tokens, user payloads, PII, tenant IDs, raw errors, or internal stack traces
  • non-robust workloads in Node runtimes, including missing abort signals, missing timeouts, unbounded promises, unbounded queues, unbounded response bodies, and event-loop blocking work

Files:

  • ops/production-regression-loop.test.ts
  • convex/couplingJobAndReveal.realtest.ts
  • convex/attestation.realtest.ts
  • ops/production-regression-loop.ts
  • convex/http.ts
  • convex/yucpLicenses.ts
  • convex/schema.ts
  • convex/attestation.ts
**/*test*

⚙️ CodeRabbit configuration file

**/*test*: Security still matters in tests.
Flag real-looking credentials, copied production tokens, unsafe fixtures with live endpoints, disabled TLS verification without isolation, frontend snapshots containing sensitive data, test logs that reveal secrets or PII, and tests that normalize insecure patterns.
Test-only bypasses must be isolated, obvious, and impossible to activate in production.

Files:

  • ops/production-regression-loop.test.ts
  • convex/couplingJobAndReveal.realtest.ts
  • convex/attestation.realtest.ts
  • convex/attestation.ts
🔇 Additional comments (3)
convex/schema.ts (1)

3161-3166: LGTM!

convex/attestation.realtest.ts (1)

1-293: LGTM!

convex/http.ts (1)

1911-1915: 🎯 Functional Correctness

No duplicate declarations to remove — the two let body declarations are in separate route handlers, and anchors only appears once in the record payload.

			> Likely an incorrect or invalid review comment.

Comment thread convex/attestation.ts
Comment thread convex/attestation.ts
Comment thread convex/attestation.ts
Comment thread convex/couplingJobAndReveal.realtest.ts
Comment thread convex/http.ts Outdated
Comment thread convex/http.ts Outdated
Comment thread convex/schema.ts
Comment thread convex/yucpLicenses.ts Outdated
Comment thread convex/yucpLicenses.ts
Address review feedback by checking every attestation row for a license subject, moving block records when identity nodes merge, and keeping nodes blocked while any active block remains.

Adds regressions for reused license subjects, merged block records, and multi-block appeal state.

Verified with bun audit, bun run lint, bun run typecheck, bun run test:convex, bun run test:external-integrations, and bun run test:ci.

@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: 8dcf0e82ee

ℹ️ 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 thread convex/attestation.ts Outdated

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

Caution

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

⚠️ Outside diff range comments (1)
convex/attestation.ts (1)

54-64: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Recompute survivor block status from the moved block records.

Lines 54-60 move block rows to intoId, but lines 62-64 only preserve blocking when either node status was already 'blocked'. If fromId has an active block row while its node status is stale/'active', the active block is moved and the survivor remains unblocked, so unlock gating can return false.

Proposed fix
   const blocks = await ctx.db
     .query('blocked_identities')
     .withIndex('by_identity_node', (q) => q.eq('identityNodeId', fromId))
     .collect();
+  const existingIntoBlocks = await ctx.db
+    .query('blocked_identities')
+    .withIndex('by_identity_node', (q) => q.eq('identityNodeId', intoId))
+    .collect();
+  const hasActiveBlock =
+    blocks.some((block) => block.status === 'active') ||
+    existingIntoBlocks.some((block) => block.status === 'active');
   for (const block of blocks) {
     await ctx.db.patch(block._id, { identityNodeId: intoId, updatedAt: now });
   }
   await ctx.db.patch(fromId, { status: 'active', mergedFromNodeId: intoId, updatedAt: now });
-  if (fromNode?.status === 'blocked' || intoNode?.status === 'blocked') {
+  if (fromNode?.status === 'blocked' || intoNode?.status === 'blocked' || hasActiveBlock) {
     await ctx.db.patch(intoId, { status: 'blocked', updatedAt: now });
   }

As per path instructions, **/*.{ts,tsx,js,jsx}: Act as a strict TypeScript and JavaScript security auditor.

Source: Path instructions


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a8c01f2e-79f5-407e-956e-348c54a0bca0

📥 Commits

Reviewing files that changed from the base of the PR and between 40ada9c and 8dcf0e8.

📒 Files selected for processing (2)
  • convex/attestation.realtest.ts
  • convex/attestation.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Type Check
  • GitHub Check: Test
  • GitHub Check: Workers Builds: creator-assistant-dashboard
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx,js,jsx}: Act as a strict TypeScript and JavaScript security auditor.
Aggressively flag:

  • SSRF, injection, XSS, prototype pollution, insecure redirects, unsafe dynamic imports, eval-like APIs, and shell command construction
  • API routes, server actions, RPC handlers, middleware, loaders, actions, edge functions, and webhooks without explicit authentication, authorization, CSRF protection where applicable, tenant isolation, and input validation
  • unsafe JSON parsing, schema gaps, mass assignment, confused-deputy issues, trusting client-controlled IDs or roles, and unsafe use of headers or cookies
  • tokens, secrets, private config, server-only env vars, API keys, tenant data, PII, or internal endpoints exposed to client bundles, hydration payloads, browser storage, source maps, analytics, logs, errors, or public assets
  • logging of request headers, cookies, tokens, user payloads, PII, tenant IDs, raw errors, or internal stack traces
  • non-robust workloads in Node runtimes, including missing abort signals, missing timeouts, unbounded promises, unbounded queues, unbounded response bodies, and event-loop blocking work

Files:

  • convex/attestation.realtest.ts
  • convex/attestation.ts
**/*test*

⚙️ CodeRabbit configuration file

**/*test*: Security still matters in tests.
Flag real-looking credentials, copied production tokens, unsafe fixtures with live endpoints, disabled TLS verification without isolation, frontend snapshots containing sensitive data, test logs that reveal secrets or PII, and tests that normalize insecure patterns.
Test-only bypasses must be isolated, obvious, and impossible to activate in production.

Files:

  • convex/attestation.realtest.ts
  • convex/attestation.ts
🔇 Additional comments (2)
convex/attestation.ts (1)

263-276: LGTM!

Also applies to: 410-426

convex/attestation.realtest.ts (1)

44-70: LGTM!

Also applies to: 121-131, 158-192, 296-325

Validate attestation relay payloads before mutation, bind consumed nonces back to submitted correlation IDs, and keep internal identifiers out of relay responses.

Make identity resolution deterministic by using durable anchors for node selection, preserving soft anchors as labels, and resolving reused license subjects through their latest attestation.

Harden coupling seed derivation with blank-secret fallback, bounded responses, redirect rejection, and AbortController timeouts. Add regressions for relay validation, block-aware unlock denial, soft-anchor non-merges, and latest-node payment/proof linking.

@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: 89a57b6a4c

ℹ️ 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 thread convex/yucpLicenses.ts Outdated
Comment thread convex/yucpLicenses.ts Outdated

@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: 6

Caution

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

⚠️ Outside diff range comments (1)
convex/yucpLicenses.ts (1)

1247-1250: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Use a null-prototype map for relay-controlled asset keys.

assetPath ultimately comes from request/relay data. With {}, keys like toString or __proto__ can resolve inherited properties and make the later seed lookup treat a non-seed value as present. Use a null-prototype object or Map, and only accept seeds for requested paths.

Suggested fix
-    const data = JSON.parse(text) as { seeds?: { assetPath: string; seedHex: string }[] };
+    const data = JSON.parse(text) as { seeds?: { assetPath?: unknown; seedHex?: unknown }[] };
     if (!Array.isArray(data?.seeds)) {
       return null;
     }
-    const map: Record<string, string> = {};
+    const requestedAssetPaths = new Set(assetPaths);
+    const map: Record<string, string> = Object.create(null);
     for (const seed of data.seeds) {
-      if (seed?.assetPath && /^[0-9a-f]{64}$/i.test(seed?.seedHex ?? '')) {
-        map[seed.assetPath] = seed.seedHex.toLowerCase();
+      const assetPath = seed?.assetPath;
+      const seedHex = seed?.seedHex;
+      if (
+        typeof assetPath === 'string' &&
+        requestedAssetPaths.has(assetPath) &&
+        typeof seedHex === 'string' &&
+        /^[0-9a-f]{64}$/i.test(seedHex)
+      ) {
+        map[assetPath] = seedHex.toLowerCase();
       }
     }

As per path instructions, **/*.{ts,tsx,js,jsx} reviews must aggressively flag prototype-pollution risks and unsafe client-controlled keys.

Source: Path instructions


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5d255592-665f-4a73-b305-48bbf8f5409d

📥 Commits

Reviewing files that changed from the base of the PR and between 8dcf0e8 and 89a57b6.

📒 Files selected for processing (7)
  • convex/attestation.realtest.ts
  • convex/attestation.ts
  • convex/couplingJobAndReveal.realtest.ts
  • convex/http.ts
  • convex/httpSurface.behavior.test.ts
  • convex/protectedUnlock.realtest.ts
  • convex/yucpLicenses.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Workers Builds: creator-assistant-dashboard
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx,js,jsx}: Act as a strict TypeScript and JavaScript security auditor.
Aggressively flag:

  • SSRF, injection, XSS, prototype pollution, insecure redirects, unsafe dynamic imports, eval-like APIs, and shell command construction
  • API routes, server actions, RPC handlers, middleware, loaders, actions, edge functions, and webhooks without explicit authentication, authorization, CSRF protection where applicable, tenant isolation, and input validation
  • unsafe JSON parsing, schema gaps, mass assignment, confused-deputy issues, trusting client-controlled IDs or roles, and unsafe use of headers or cookies
  • tokens, secrets, private config, server-only env vars, API keys, tenant data, PII, or internal endpoints exposed to client bundles, hydration payloads, browser storage, source maps, analytics, logs, errors, or public assets
  • logging of request headers, cookies, tokens, user payloads, PII, tenant IDs, raw errors, or internal stack traces
  • non-robust workloads in Node runtimes, including missing abort signals, missing timeouts, unbounded promises, unbounded queues, unbounded response bodies, and event-loop blocking work

Files:

  • convex/protectedUnlock.realtest.ts
  • convex/couplingJobAndReveal.realtest.ts
  • convex/httpSurface.behavior.test.ts
  • convex/yucpLicenses.ts
  • convex/attestation.realtest.ts
  • convex/http.ts
  • convex/attestation.ts
**/*test*

⚙️ CodeRabbit configuration file

**/*test*: Security still matters in tests.
Flag real-looking credentials, copied production tokens, unsafe fixtures with live endpoints, disabled TLS verification without isolation, frontend snapshots containing sensitive data, test logs that reveal secrets or PII, and tests that normalize insecure patterns.
Test-only bypasses must be isolated, obvious, and impossible to activate in production.

Files:

  • convex/protectedUnlock.realtest.ts
  • convex/couplingJobAndReveal.realtest.ts
  • convex/httpSurface.behavior.test.ts
  • convex/attestation.realtest.ts
  • convex/attestation.ts
🔇 Additional comments (6)
convex/protectedUnlock.realtest.ts (2)

4-4: LGTM!

Also applies to: 20-20, 83-83


96-126: LGTM!

Also applies to: 169-190

convex/attestation.ts (1)

20-20: LGTM!

Also applies to: 195-209, 227-237, 316-316, 379-380, 432-444

convex/attestation.realtest.ts (1)

44-62: LGTM!

Also applies to: 153-166, 331-344, 420-438

convex/http.ts (1)

1875-2172: LGTM!

Also applies to: 2210-2235, 2250-2272, 2288-2300

convex/httpSurface.behavior.test.ts (1)

57-63: LGTM!

Also applies to: 114-123, 148-148, 323-498

Comment thread convex/attestation.ts Outdated
Comment thread convex/couplingJobAndReveal.realtest.ts
Comment thread convex/httpSurface.behavior.test.ts Outdated
Comment thread convex/yucpLicenses.ts Outdated
Comment thread convex/yucpLicenses.ts Outdated
Comment thread convex/yucpLicenses.ts Outdated
Require a license subject to have a recorded attestation before protected unlock issuance so new licenses cannot bypass hardware and payment block inheritance.

Prefer YUCP_COUPLING_SERVICE_SHARED_SECRET over the legacy coupling secret during seed relay rotation, matching the API env resolver.

Keep identity merge survivors blocked when active block records are moved during node absorption, even if the absorbed node status was stale.

@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: f0e09a47ae

ℹ️ 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 thread convex/yucpLicenses.ts Outdated
Use a licenseSubject and createdAt index for latest-attestation lookups so payment anchors and coupling proofs avoid unbounded scans.

Reject non-loopback HTTP coupling seed relay endpoints before sending bearer credentials and read relay responses through a bounded stream reader.

Restore optional HTTP surface test secrets by deleting env vars that were originally unset.

@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: 9a111718b2

ℹ️ 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 thread convex/attestation.ts
Require protected unlocks to have an attestation for the same machine fingerprint hash as the unlock token so a helper-machine attestation cannot satisfy a different machine.

Persist coupling-proof license subjects and relink pending proofs when a later attestation resolves the subject to an identity node.

Repoint linked coupling proofs during identity-node merges so forensic records follow the surviving node.

@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: 6566d121b3

ℹ️ 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 thread convex/yucpLicenses.ts Outdated

@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

Caution

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

⚠️ Outside diff range comments (1)
convex/attestation.ts (1)

314-330: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Constrain block checks to the matching machine when provided.

Line 318 computes currentMachineAttested, but Lines 321-327 still scan all attestations for the license. A blocked node from another machine using the same license can make this unlock return { blocked: true, attested: true } even when machineFingerprintHash does not match. Query by_license_subject_machine when available and derive both fields from that scoped set.

As per path instructions, “RPC handlers … [need] tenant isolation, and input validation.”

Proposed fix
-    const attestations = await ctx.db
-      .query('machine_attestations')
-      .withIndex('by_license_subject', (q) => q.eq('licenseSubject', args.licenseSubject))
-      .collect();
-    const currentMachineAttested = args.machineFingerprintHash
-      ? attestations.some((att) => att.machineFingerprintHash === args.machineFingerprintHash)
-      : attestations.length > 0;
+    const attestations = args.machineFingerprintHash
+      ? await ctx.db
+          .query('machine_attestations')
+          .withIndex('by_license_subject_machine', (q) =>
+            q
+              .eq('licenseSubject', args.licenseSubject)
+              .eq('machineFingerprintHash', args.machineFingerprintHash)
+          )
+          .collect()
+      : await ctx.db
+          .query('machine_attestations')
+          .withIndex('by_license_subject', (q) => q.eq('licenseSubject', args.licenseSubject))
+          .collect();
+    const currentMachineAttested = attestations.length > 0;

Source: Path instructions


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 261c75d3-c030-453c-8586-c242a114480e

📥 Commits

Reviewing files that changed from the base of the PR and between 9a11171 and 6566d12.

📒 Files selected for processing (7)
  • convex/attestation.realtest.ts
  • convex/attestation.ts
  • convex/http.ts
  • convex/protectedBlobArchitecture.realtest.ts
  • convex/protectedUnlock.realtest.ts
  • convex/schema.ts
  • convex/yucpLicenses.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Lint
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx,js,jsx}: Act as a strict TypeScript and JavaScript security auditor.
Aggressively flag:

  • SSRF, injection, XSS, prototype pollution, insecure redirects, unsafe dynamic imports, eval-like APIs, and shell command construction
  • API routes, server actions, RPC handlers, middleware, loaders, actions, edge functions, and webhooks without explicit authentication, authorization, CSRF protection where applicable, tenant isolation, and input validation
  • unsafe JSON parsing, schema gaps, mass assignment, confused-deputy issues, trusting client-controlled IDs or roles, and unsafe use of headers or cookies
  • tokens, secrets, private config, server-only env vars, API keys, tenant data, PII, or internal endpoints exposed to client bundles, hydration payloads, browser storage, source maps, analytics, logs, errors, or public assets
  • logging of request headers, cookies, tokens, user payloads, PII, tenant IDs, raw errors, or internal stack traces
  • non-robust workloads in Node runtimes, including missing abort signals, missing timeouts, unbounded promises, unbounded queues, unbounded response bodies, and event-loop blocking work

Files:

  • convex/protectedBlobArchitecture.realtest.ts
  • convex/http.ts
  • convex/protectedUnlock.realtest.ts
  • convex/attestation.realtest.ts
  • convex/schema.ts
  • convex/yucpLicenses.ts
  • convex/attestation.ts
**/*test*

⚙️ CodeRabbit configuration file

**/*test*: Security still matters in tests.
Flag real-looking credentials, copied production tokens, unsafe fixtures with live endpoints, disabled TLS verification without isolation, frontend snapshots containing sensitive data, test logs that reveal secrets or PII, and tests that normalize insecure patterns.
Test-only bypasses must be isolated, obvious, and impossible to activate in production.

Files:

  • convex/protectedBlobArchitecture.realtest.ts
  • convex/protectedUnlock.realtest.ts
  • convex/attestation.realtest.ts
  • convex/attestation.ts
🔇 Additional comments (5)
convex/schema.ts (1)

2666-2683: LGTM!

Also applies to: 2769-2777

convex/yucpLicenses.ts (1)

1518-1518: LGTM!

Also applies to: 1538-1551

convex/attestation.realtest.ts (1)

470-489: LGTM!

convex/protectedUnlock.realtest.ts (1)

26-29: LGTM!

Also applies to: 82-92, 111-111, 126-126, 169-190

convex/protectedBlobArchitecture.realtest.ts (1)

176-176: LGTM!

Comment thread convex/attestation.ts
Comment thread convex/http.ts Outdated
Scope protected-unlock block evaluation to the attestation rows for the current machine fingerprint when a machine hash is provided, so a block on another machine using the same license does not deny a clean current-machine unlock.

Add a regression covering a clean current machine with a blocked different machine on the same license subject.

@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: 9af5849feb

ℹ️ 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 thread convex/http.ts Outdated
Preserve private coupling-service base paths when deriving seeds so reverse-proxy prefixes are not dropped.

Relink delayed coupling proofs by the submitted correlation id and matching license subject instead of claiming every pending proof for the same license.

Require attestation relay records to include the machine fingerprint hash so protected unlocks can satisfy the same-machine attestation gate.

@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: 50b4b209a8

ℹ️ 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 thread convex/attestation.ts

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

Caution

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

⚠️ Outside diff range comments (1)
convex/yucpLicenses.ts (1)

1296-1302: 🔒 Security & Privacy | 🟠 Major

Use an own-key map for coupling seeds
assetPath is untrusted, and Record<string, string> = {} lets inherited keys like constructor, toString, or __proto__ read as truthy values when no seed exists. Switch this to Map or a null-prototype object with hasOwn checks. convex/yucpLicenses.ts:1296-1302, 1407-1415

Source: Path instructions


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d9b2e872-4d8a-4799-b425-b19391bb3d7c

📥 Commits

Reviewing files that changed from the base of the PR and between 6566d12 and 50b4b20.

📒 Files selected for processing (7)
  • convex/attestation.realtest.ts
  • convex/attestation.ts
  • convex/couplingJobAndReveal.realtest.ts
  • convex/http.ts
  • convex/httpSurface.behavior.test.ts
  • convex/protectedUnlock.realtest.ts
  • convex/yucpLicenses.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Workers Builds: creator-assistant-dashboard
  • GitHub Check: Analyze (javascript-typescript)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx,js,jsx}: Act as a strict TypeScript and JavaScript security auditor.
Aggressively flag:

  • SSRF, injection, XSS, prototype pollution, insecure redirects, unsafe dynamic imports, eval-like APIs, and shell command construction
  • API routes, server actions, RPC handlers, middleware, loaders, actions, edge functions, and webhooks without explicit authentication, authorization, CSRF protection where applicable, tenant isolation, and input validation
  • unsafe JSON parsing, schema gaps, mass assignment, confused-deputy issues, trusting client-controlled IDs or roles, and unsafe use of headers or cookies
  • tokens, secrets, private config, server-only env vars, API keys, tenant data, PII, or internal endpoints exposed to client bundles, hydration payloads, browser storage, source maps, analytics, logs, errors, or public assets
  • logging of request headers, cookies, tokens, user payloads, PII, tenant IDs, raw errors, or internal stack traces
  • non-robust workloads in Node runtimes, including missing abort signals, missing timeouts, unbounded promises, unbounded queues, unbounded response bodies, and event-loop blocking work

Files:

  • convex/yucpLicenses.ts
  • convex/httpSurface.behavior.test.ts
  • convex/protectedUnlock.realtest.ts
  • convex/couplingJobAndReveal.realtest.ts
  • convex/attestation.realtest.ts
  • convex/http.ts
  • convex/attestation.ts
**/*test*

⚙️ CodeRabbit configuration file

**/*test*: Security still matters in tests.
Flag real-looking credentials, copied production tokens, unsafe fixtures with live endpoints, disabled TLS verification without isolation, frontend snapshots containing sensitive data, test logs that reveal secrets or PII, and tests that normalize insecure patterns.
Test-only bypasses must be isolated, obvious, and impossible to activate in production.

Files:

  • convex/httpSurface.behavior.test.ts
  • convex/protectedUnlock.realtest.ts
  • convex/couplingJobAndReveal.realtest.ts
  • convex/attestation.realtest.ts
  • convex/attestation.ts
🔇 Additional comments (7)
convex/attestation.ts (1)

103-118: LGTM!

Also applies to: 296-301, 320-343

convex/yucpLicenses.ts (1)

1268-1268: LGTM!

Also applies to: 1340-1340, 1518-1551

convex/attestation.realtest.ts (1)

486-522: LGTM!

convex/couplingJobAndReveal.realtest.ts (1)

384-398: Reinstate auth and method assertions in this inline relay mock.

This stub only checks the URL. If the action stops sending POST or drops the bearer secret, this regression still passes and re-normalizes the insecure relay path the suite already tightened elsewhere. Reuse the stricter helper or assert method and Authorization here too. As per path instructions, "Security still matters in tests" and flag "tests that normalize insecure patterns".

Source: Path instructions

convex/http.ts (1)

1912-1912: LGTM!

Also applies to: 2034-2114

convex/httpSurface.behavior.test.ts (1)

401-401: LGTM!

Also applies to: 416-454

convex/protectedUnlock.realtest.ts (1)

116-151: LGTM!

Also applies to: 263-286

Add bearer-protected internal HTTP routes for closed-service identity block creation and review so confirmed leaked-trace evidence can reach the block ledger in production.

Return the created block id from the internal flag mutation so the review route can promote or reverse the pending record without manual database edits.

Cover both routes in the Convex HTTP surface regression tests.

@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: 29a57c54e9

ℹ️ 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 thread convex/attestation.ts

@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: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 99f0e497-d17c-4b6e-9b83-4a84750b001b

📥 Commits

Reviewing files that changed from the base of the PR and between 50b4b20 and 29a57c5.

📒 Files selected for processing (3)
  • convex/attestation.ts
  • convex/http.ts
  • convex/httpSurface.behavior.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Test
  • GitHub Check: Type Check
  • GitHub Check: Workers Builds: creator-assistant-dashboard
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

**/*.{ts,tsx,js,jsx}: Act as a strict TypeScript and JavaScript security auditor.
Aggressively flag:

  • SSRF, injection, XSS, prototype pollution, insecure redirects, unsafe dynamic imports, eval-like APIs, and shell command construction
  • API routes, server actions, RPC handlers, middleware, loaders, actions, edge functions, and webhooks without explicit authentication, authorization, CSRF protection where applicable, tenant isolation, and input validation
  • unsafe JSON parsing, schema gaps, mass assignment, confused-deputy issues, trusting client-controlled IDs or roles, and unsafe use of headers or cookies
  • tokens, secrets, private config, server-only env vars, API keys, tenant data, PII, or internal endpoints exposed to client bundles, hydration payloads, browser storage, source maps, analytics, logs, errors, or public assets
  • logging of request headers, cookies, tokens, user payloads, PII, tenant IDs, raw errors, or internal stack traces
  • non-robust workloads in Node runtimes, including missing abort signals, missing timeouts, unbounded promises, unbounded queues, unbounded response bodies, and event-loop blocking work

Files:

  • convex/http.ts
  • convex/httpSurface.behavior.test.ts
  • convex/attestation.ts
**/*test*

⚙️ CodeRabbit configuration file

**/*test*: Security still matters in tests.
Flag real-looking credentials, copied production tokens, unsafe fixtures with live endpoints, disabled TLS verification without isolation, frontend snapshots containing sensitive data, test logs that reveal secrets or PII, and tests that normalize insecure patterns.
Test-only bypasses must be isolated, obvious, and impossible to activate in production.

Files:

  • convex/httpSurface.behavior.test.ts
  • convex/attestation.ts
🔇 Additional comments (2)
convex/http.ts (1)

51-56: LGTM!

Also applies to: 71-71, 1953-1962, 2197-2252, 2386-2447

convex/httpSurface.behavior.test.ts (1)

63-64: LGTM!

Also applies to: 555-627

Comment thread convex/attestation.ts
Reject identity block flags for missing identity nodes so the block ledger cannot accumulate orphaned review records.

Require at least two durable anchors before an identity block review can promote a node to active blocking status.

Update attestation and protected-unlock regressions so successful promotions use block-eligible identities and single-anchor nodes stay unblocked.
@Yeusepe
Yeusepe merged commit ba88dc7 into main Jun 25, 2026
9 checks passed
@Yeusepe
Yeusepe deleted the feat/attestation-identity-relay branch June 25, 2026 19:05
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Creator Assistant Roadmap Jun 25, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant