Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/admin/generate-first-action-receipt.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function paidOrApproved(claim) {
function preflight(claim) {
if (!paidOrApproved(claim)) return 'FIRST_ACTION_REQUIRES_PAID_CLAIM';
if (!claim.tenant_signer_ens || !claim.tenant_signer_kid) return 'TENANT_SIGNER_ENS_REQUIRED';
if (claim.tenant_signer_record_status !== 'records_verified') return 'TENANT_SIGNER_RECORDS_NOT_VERIFIED';
if (claim.tenant_signer_record_status !== 'records_verified' && claim.tenant_signer_record_status !== 'verified') return 'TENANT_SIGNER_RECORDS_NOT_VERIFIED';
if (!claim.genesis_receipt_id) return 'GENESIS_RECEIPT_REQUIRED';
if ('tenant_proof_status' in claim && claim.tenant_proof_status && claim.tenant_proof_status !== 'verified') return 'TENANT_PROOF_NOT_VERIFIED';
return null;
Expand Down
32 changes: 32 additions & 0 deletions api/admin/run-activation-pipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const db = require('../../lib/db');
const { requireAdminAuth } = require('./_auth');
const pinAgentCards = require('./pin-agent-cards');
const generateGenesisReceipt = require('./generate-genesis-receipt');
const generateFirstActionReceipt = require('./generate-first-action-receipt');

function makeInternalRes() {
return {
Expand All @@ -26,6 +27,17 @@ function paymentStep(claim) {
return claim.status === 'paid' || claim.status === 'cards_pinned' || claim.payment_status === 'paid' || claim.paid_at ? 'already_paid' : 'payment_required';
}

function recordsVerified(claim) {
return claim.tenant_signer_record_status === 'records_verified' || claim.tenant_signer_record_status === 'verified';
}

function canAttemptFirstAction(claim) {
if (!claim.genesis_receipt_id) return false;
if (!recordsVerified(claim)) return false;
if ('tenant_proof_status' in claim && claim.tenant_proof_status && claim.tenant_proof_status !== 'verified') return false;
return true;
}

module.exports = async function handler(req, res) {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.setHeader('Cache-Control', 'no-store');
Expand All @@ -52,6 +64,8 @@ module.exports = async function handler(req, res) {
agent_cards: 'not_started',
genesis_receipt: claim.genesis_receipt_id ? 'already_generated' : 'not_started',
tenant_action_proof: claim.tenant_proof_status || 'not_submitted',
first_action_receipt: claim.first_action_receipt_status || 'not_generated',
errors: {},
};

if (steps.payment !== 'already_paid') {
Expand All @@ -71,6 +85,7 @@ module.exports = async function handler(req, res) {
steps.agent_cards = pinRes.body && pinRes.body.status === 'ALREADY_PINNED' ? 'cards_pinned' : 'cards_pinned';
} else {
steps.agent_cards = pinRes.body && pinRes.body.status ? pinRes.body.status : 'pinning_not_completed';
steps.errors.agent_cards = steps.agent_cards;
}
}

Expand All @@ -86,6 +101,23 @@ module.exports = async function handler(req, res) {
steps.genesis_receipt = 'already_generated';
} else {
steps.genesis_receipt = genesisRes.body && genesisRes.body.status ? genesisRes.body.status : 'not_generated';
steps.errors.genesis_receipt = steps.genesis_receipt;
}
}

claimResult = await db.query('select * from claim_requests where claim_id = $1 limit 1', [claimId]);
claim = claimResult.rows[0] || claim;
if (claim.first_action_receipt_status === 'verified') {
steps.first_action_receipt = 'verified';
} else if (claim.first_action_receipt_status === 'challenge_ready') {
steps.first_action_receipt = 'challenge_ready';
} else if (canAttemptFirstAction(claim)) {
const firstActionRes = await runHandler(generateFirstActionReceipt, { method: 'POST', headers: { 'x-admin-api-key': req.headers['x-admin-api-key'] || process.env.ADMIN_API_KEY }, body: { claimId } });
if (firstActionRes.statusCode >= 200 && firstActionRes.statusCode < 300) {
steps.first_action_receipt = firstActionRes.body && firstActionRes.body.status ? firstActionRes.body.status : 'challenge_ready';
} else {
steps.first_action_receipt = firstActionRes.body && firstActionRes.body.status ? firstActionRes.body.status : 'not_generated';
steps.errors.first_action_receipt = steps.first_action_receipt;
}
}

Expand Down
23 changes: 22 additions & 1 deletion api/admin/verify-managed-ens-publication.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,25 @@
const db = require('../../lib/db');
const { requireAdminAuth } = require('./_auth');
const { verifyManagedEnsPublication } = require('../../lib/claims/managed-ens-publication');
const runActivationPipeline = require('./run-activation-pipeline');

function makeInternalRes() {
return {
statusCode: 200,
body: null,
headers: {},
setHeader(name, value) { this.headers[String(name).toLowerCase()] = value; },
status(code) { this.statusCode = code; return this; },
json(payload) { this.body = payload; return this; },
};
}

async function advanceActivation(req, claimId) {
const pipelineRes = makeInternalRes();
await runActivationPipeline({ method: 'POST', headers: { 'x-admin-api-key': req.headers['x-admin-api-key'] || process.env.ADMIN_API_KEY }, body: { claimId } }, pipelineRes);
if (pipelineRes.statusCode >= 200 && pipelineRes.statusCode < 300 && pipelineRes.body && pipelineRes.body.ok) return pipelineRes.body.steps || {};
return { errors: { activation_pipeline: pipelineRes.body && pipelineRes.body.status ? pipelineRes.body.status : 'ACTIVATION_PIPELINE_FAILED' } };
}

module.exports = async function handler(req, res) {
res.setHeader('Content-Type', 'application/json; charset=utf-8');
Expand All @@ -19,12 +38,14 @@ module.exports = async function handler(req, res) {
if (verification.ok) {
await db.query(
`update claim_requests
set managed_ens_publication_status = 'verified', tenant_signer_record_status = 'records_verified',
set managed_ens_publication_status = 'verified', tenant_signer_record_status = 'verified',

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 Preserve the submit-compatible signer status

Setting managed-ENS claims to the new tenant_signer_record_status = 'verified' leaves them incompatible with the tenant receipt-submission flow: api/claims/submit-first-action-receipt.js still rejects anything other than records_verified before verifying the signed first-action receipt. In the managed flow this endpoint now generates a challenge successfully, but the tenant cannot submit the signed receipt until another path rewrites the status, so use the existing records_verified value or update all consumers that gate on it.

Useful? React with 👍 / 👎.

tenant_signer_records_verified_at = now(), managed_ens_verified_at = now(),
managed_ens_publication_error = null, tenant_signer_verification_error = null, updated_at = now()
where claim_id = $1`,
[claimId]
);
const advanced = await advanceActivation(req, claimId);
return res.status(200).json({ ok: true, claim_id: claimId, managed_ens_publication_status: 'verified', tenant_signer_record_status: 'verified', verification, advanced });
} else {
await db.query(
`update claim_requests
Expand Down
3 changes: 2 additions & 1 deletion api/claims/status.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module.exports = async function handler(req, res) {
} catch (_error) {
cards = [];
}
const recordsVerified = claim.tenant_signer_record_status === 'records_verified' || claim.tenant_signer_record_status === 'verified';
const paymentConfirmed = claim.status === 'paid' || claim.status === 'cards_pinned' || claim.status === 'active' || claim.payment_status === 'paid' || Boolean(claim.paid_at);
const pipeline = {
tenant_signing_identity: claim.tenant_signer_ens ? 'generated' : 'missing',
Expand All @@ -55,7 +56,7 @@ module.exports = async function handler(req, res) {
genesis_receipt: claim.genesis_receipt_id ? 'generated' : 'not_generated',
tenant_action_proof: claim.tenant_proof_status || 'not_submitted',
first_action_receipt: claim.first_action_receipt_status || 'not_generated',
agent_live: paymentConfirmed && claim.tenant_signer_record_status === 'records_verified' && cardsStatus(cards) === 'cards_pinned' && claim.genesis_receipt_id && claim.tenant_proof_status === 'verified' && (claim.first_action_receipt_status === 'verified' || typeof claim.first_action_receipt_status === 'undefined') ? 'live' : 'not_live',
agent_live: paymentConfirmed && recordsVerified && cardsStatus(cards) === 'cards_pinned' && claim.genesis_receipt_id && claim.tenant_proof_status === 'verified' && (claim.first_action_receipt_status === 'verified' || typeof claim.first_action_receipt_status === 'undefined') ? 'live' : 'not_live',
};
return res.status(200).json({ ok: true, read_only: true, claim: { ...stripClaimSecrets(claim), cardsStatus: cardsStatus(cards), managed_ens_publication: { status: claim.managed_ens_publication_status || 'not_started', signer_ens: claim.tenant_signer_ens || null, parent_namespace: claim.managed_ens_parent_namespace || null, record_names: Object.keys(claim.managed_ens_required_txt_records || claim.tenant_signer_txt_records || {}), helper_copy: 'The operator must publish the generated TXT records to ENS before signer verification can pass.', verified_at: claim.managed_ens_verified_at || null, error: claim.managed_ens_publication_error || null } }, pipeline, cards });
} catch (_error) {
Expand Down
Loading
Loading