Skip to content

Add managed ENS publication helper#388

Merged
GsCommand merged 1 commit into
mainfrom
codex/add-managed-ens-publication-helper
Jun 17, 2026
Merged

Add managed ENS publication helper#388
GsCommand merged 1 commit into
mainfrom
codex/add-managed-ens-publication-helper

Conversation

@GsCommand

Copy link
Copy Markdown
Contributor

Motivation

  • Provide a safe operator-facing helper for managed namespace claims to prepare required ENS TXT records and verify live TXT records without performing any onchain writes.
  • Make the managed signer ENS workflow visible in the activation pipeline and public claim status so operators can publish TXT records and confirm verification manually.

Description

  • Add migration db/migrations/011_managed_ens_publication.sql to add managed_ens_publication_status, managed_ens_parent_namespace, managed_ens_publication_instructions, managed_ens_required_txt_records, managed_ens_verified_at, and managed_ens_publication_error to claim_requests using alter table if exists / add column if not exists.
  • Implement lib/claims/managed-ens-publication.js with buildManagedEnsPublicationPackage(claim) and verifyManagedEnsPublication(claim) that validate managed-namespace safety, build the publication package (signer ENS, parent namespace, required TXT records, agent records, and operator instructions), and verify live TXT records by reusing the existing signer-record resolver/comparator.
  • Add admin endpoints POST /api/admin/prepare-managed-ens-publication and POST /api/admin/verify-managed-ens-publication which require admin auth, load the claim, persist publication state/records/instructions, and update verification status based on live TXT checks without broadcasting transactions.
  • Surface managed publication state in the activation pipeline and public claim status via updates to api/admin/run-activation-pipeline.js and api/claims/status.js, and add UI controls and record display to public/admin/claims.html for preparing and verifying TXT records.
  • Add tests in tests/managed-ens-publication.test.js covering preparation, safety validations (mode, missing ENS, root ENS rejection, unapproved parent, receipt signer mismatch), successful verification updates, failed verification behavior, and public status exposure.
  • Safety: the helper is strictly limited to activation_mode = managed_namespace, enforces approved managed parents, rejects tenant root ENS and mismatched cl.receipt.signer, requires public key/kid/canonicalization, and performs no onchain writes, transaction signing, wallet connections, or private key custody.

Testing

  • Ran unit/integration suite with npm test -- --test-reporter=spec and all tests passed (166 tests, 0 failures).
  • Ran targeted test and debug runs of node --test tests/managed-ens-publication.test.js during development and the new tests passed.
  • Ran link checks with npm run check:links and confirmed local links/assets resolved successfully.

Codex Task

@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
commandlayer-commandlayer-org Ready Ready Preview, Comment Jun 17, 2026 12:32am
commandlayer-org Ready Ready Preview, Comment Jun 17, 2026 12:32am
commandlayer-org111 Ready Ready Preview, Comment Jun 17, 2026 12:32am

Request 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: cc71e58231

ℹ️ 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".


const { buildSignerRecords, CANONICALIZATION, resolveRequiredSignerRecords, compareSignerRecords } = require('./signer-records');

const APPROVED_MANAGED_PARENTS = ['attestagent.eth', 'approveagent.eth', 'verifyagent.eth', 'authorizeagent.eth'];

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 Accept generated trust parents in the managed allow-list

For CL-mode claims submitted with the current Trust Verification pack, public/claim.html selects all TRUST_PARENTS in order, and the first generated tenant signer is *.signagent.eth; this allow-list omits signagent.eth (and several other generated trust parents), so buildManagedEnsPublicationPackage() rejects those normal managed claims as MANAGED_PARENT_NOT_APPROVED before operators can prepare TXT records. Please keep this list aligned with the generated managed parents or the helper is unusable for the default trust pack.

Useful? React with 👍 / 👎.

Comment thread public/admin/claims.html

async function prepareManagedEnsPublication(){if(!s.selected){s.error='Request failed: 400';renderDetail();return;}s.error=null;const r=await fetch('/api/admin/prepare-managed-ens-publication',{method:'POST',headers:headers(),body:JSON.stringify({claimId:s.selected})});const d=await r.json().catch(()=>({}));if(!r.ok||!d.ok){s.error=d?.status||'MANAGED_ENS_PUBLICATION_PREPARE_FAILED';renderDetail();return;}s.managedEnsPublication=d.publication;await loadDetail(s.selected);}
async function verifyManagedEnsPublication(){if(!s.selected){s.error='Request failed: 400';renderDetail();return;}s.error=null;const r=await fetch('/api/admin/verify-managed-ens-publication',{method:'POST',headers:headers(),body:JSON.stringify({claimId:s.selected})});const d=await r.json().catch(()=>({}));if(!r.ok||!d.ok){s.error=d?.status||'MANAGED_ENS_PUBLICATION_VERIFY_FAILED';renderDetail();return;}s.managedEnsPublication=null;await loadDetail(s.selected);}
function txtRecordBlocks(claim){const records=(s.managedEnsPublication&&s.managedEnsPublication.required_txt_records)||claim.managed_ens_required_txt_records||claim.tenant_signer_txt_records||{};return Object.entries(records).map(([k,v])=>`<div class='panel' style='margin:8px 0'><div><strong>${k}</strong> <button class='btn btn-muted copy-url' data-url='${String(v).replaceAll("'",'&#39;')}'>Copy value</button></div><pre class='mono' style='white-space:pre-wrap;overflow-wrap:anywhere;'>${v}</pre></div>`).join('')||'<p class="muted">Prepare TXT records to display required values.</p>';}

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 Clear cached TXT records when loading another claim

When an operator prepares claim A and then opens claim B, loadDetail() does not clear s.managedEnsPublication, and this expression always prefers the cached package over the loaded claim's records. The detail panel can therefore display/copy A's TXT values for B until a verification clears the cache or the page reloads, risking publication of the wrong ENS records.

Useful? React with 👍 / 👎.

Comment on lines +41 to +44
'cl.capability': 'attest',
'cl.runtime': 'https://runtime.commandlayer.org',
'cl.verifier': 'https://runtime.commandlayer.org/verify',
'cl.trust_verification_entry': 'https://runtime.commandlayer.org/trust-verification/attest/v1.0.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.

P2 Badge Derive agent records from the signer capability

For managed claims whose signer is one of the other allowed parents, such as acme.approveagent.eth or acme.verifyagent.eth, the publication package still tells the operator to publish cl.capability=attest and the attest trust-verification entry. That makes the helper emit incorrect TXT guidance for valid non-attest managed signer names, so these values should be derived from the signer parent/capability instead of hard-coded.

Useful? React with 👍 / 👎.

}
return res.status(200).json({ ok: true, claim_id: claimId, verification });
} catch (error) {
try { await db.query(`update claim_requests set managed_ens_publication_status = 'failed', managed_ens_publication_error = $2, updated_at = now() where claim_id = $1`, [claimId, error.message]); } catch (_) {}

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 Store a bounded error code for resolver failures

When the ENS/RPC resolver throws, this catch persists the raw error.message into managed_ens_publication_error, and api/claims/status.js returns that field to claim-token callers. Unlike the existing signer-record verifier, which stores fixed error codes, this can expose arbitrary provider exception text in claimant-visible status after a transient resolver failure; persist a stable code instead.

Useful? React with 👍 / 👎.

Comment on lines +30 to +32
`update claim_requests
set managed_ens_publication_status = $2, managed_ens_publication_error = $3,
tenant_signer_verification_error = $3, updated_at = now()

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 Keep signer status in sync on failed verification

When live TXT records are present but mismatched, this branch records only the managed publication status/error and leaves tenant_signer_record_status at its previous value (commonly records_generated). Since both the public pipeline and admin list read tenant_signer_record_status for ENS record state, an operator-run failed verification still surfaces stale signer-record status instead of records_mismatch/records_unavailable; update it the same way the existing signer-record verifier does.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant