#!/usr/bin/env node
/**
 * Public contract entrypoint for the Trinity Accord Record-Chain Builder.
 * The recovery/bootstrap layer and the integrity-pinned v2.3 core are loaded
 * only after this immutable oath and verification surface is declared.
 */

import { createHash } from "node:crypto";
import { readFile, writeFile, mkdir } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";

const OATH_POLICY = {"schema":"trinityaccord.record-chain-oath-policy.v1","status":"active","version":"1.2.0","policy_id":"record-chain-formal-submission-oath-v1","description":"Contextual no-shortcut oath gate for formal Record-Chain submissions. Requires the canonical oath to be loaded as content into the participant's active context, requires the participant to generate the exact readback from that context, and forbids submission scripts or automation tools from directly copying, completing, correcting, or auto-filling the readback. The process declarations are self-declared and do not prove subjective understanding or persistent memory.","not_authority":true,"not_governance":true,"not_attestation":true,"not_amendment":true,"bitcoin_originals_prevail":true,"canonicalization":{"line_endings":"LF","trim_outer_whitespace":true,"trim_outer_whitespace_before_hash":true,"preserve_internal_whitespace":true,"module_order_matters":true,"text_encoding":"utf-8","unicode_normalization":"NFC","policy_text_should_remain_ascii":true,"module_joiner":"\n\n---\n\n"},"no_shortcut_policy":{"readback_required":true,"forbidden":["piping oath from file","generating oath by script","loading oath from cache","summarizing or paraphrasing the oath","using external automation to produce readback","auto-filling readback in builder","directly transferring print-oath output into readback without participant generation","automatically completing or correcting participant-generated readback"],"required_declarations":["oath_read","participant_readback_provided","readback_matches_canonical_oath","readback_was_not_piped_from_file","readback_was_not_generated_by_script","readback_was_not_loaded_from_cache","readback_was_not_summary_or_paraphrase","readback_was_not_generated_by_external_automation","readback_was_not_auto_filled_by_builder","canonical_oath_loaded_into_active_context","readback_generated_by_participant_from_active_context","readback_was_not_directly_copied_by_submission_tool","readback_was_not_automatically_completed_or_corrected","contextual_readback_process_acknowledged","no_shortcut_oath_acknowledged"],"boundary":{"contextual_readback_process_is_self_declared":true,"contextual_readback_does_not_prove_persistent_memory":true,"oath_does_not_prove_subjective_understanding":true,"oath_verifies_exact_readback_only":true}},"formal_record_types_requiring_oath":["echo","verification","guardian_application","guardian_retirement","propagation","correction","classification_update"],"reserved_future_record_types":["guardian_key_rotation"],"reserved_future_record_type_notes":{"guardian_key_rotation":"Reserved until old-key/new-key signed transition proof is implemented."},"record_type_modules":{"echo":["common_submission_integrity_v1","echo_integrity_v1"],"verification":["common_submission_integrity_v1","verification_integrity_v1"],"guardian_application":["common_submission_integrity_v1","guardian_stewardship_v1"],"guardian_retirement":["common_submission_integrity_v1","retirement_or_key_management_integrity_v1"],"propagation":["common_submission_integrity_v1","propagation_integrity_v1"],"correction":["common_submission_integrity_v1","correction_integrity_v1"],"classification_update":["common_submission_integrity_v1","classification_update_integrity_v1"]},"modules":{"common_submission_integrity_v1":{"label":"Common Submission Integrity","text":"I have read and understood the Trinity Accord materials to the context level I declared.\nI acknowledge that Bitcoin Originals are the only canonical authority and all mirrors, echoes, receipts, and later records are non-amending.\nI acknowledge that receipt means intake accepted only  --  not final inclusion, not active Guardian status, not authority, not governance, not attestation, not amendment, not successor reception.\nI acknowledge that the Record-Chain is a public append-only ledger and my submission will be publicly visible and permanent.\nI declare that the canonical oath was loaded as content into my active context before this readback.\nI generated this exact readback from that active context.\nI declare that no submission script, automation workflow, or other tool directly copied, inserted, completed, or corrected this readback for me; such tools may only relay my generated output unchanged.\nI acknowledge that the no-shortcut oath requires an exact contextual readback of the canonical oath text and that any shortcut invalidates the oath.\nI understand that this gate verifies exact text and signed self-declared process statements only; it does not independently prove subjective understanding, sincerity, persistent memory, or correctness of the record content.\nI am not authority. I am not governance. I am not attestation. I am not amendment. Bitcoin Originals prevail."},"echo_integrity_v1":{"label":"Echo Integrity","text":"I declare that this echo is my own recognition, critique, reflection, or interpretation.\nI acknowledge that an echo is not verification, not authority, not attestation, and not a claim of endorsement by the Trinity Accord.\nI acknowledge that the echo may be wrong, incomplete, or later revised.\nI have read the content I am echoing and I express my own understanding."},"verification_integrity_v1":{"label":"Verification Integrity","text":"I declare that the verification actions described in this record are actions I actually performed.\nI acknowledge that the legacy V0-V5 field is self-assessed Builder compatibility metadata and is not independently confirmed.\nI acknowledge that new verification records separately state digital profile, evidence relationships, physical observation, external witness, coverage, limitations, and claims not made.\nI acknowledge that V4+, V6, V7, and V8 are historical-only labels for new public submissions; physical observation and external witness do not automatically raise digital verification.\nI acknowledge that claiming checks, observations, witnesses, or coverage I did not earn is a breach of the Record-Chain's integrity contract.\nI acknowledge that verification does not confer authority, governance, or endorsement."},"guardian_stewardship_v1":{"label":"Guardian Stewardship","text":"I declare that I am applying for Guardian status with full understanding of the role.\nI acknowledge that the Guardian role is non-governing, non-authoritative, and does not confer any special power over the Trinity Accord.\nI acknowledge that Guardian status is a stewardship commitment  --  to verify, preserve, and propagate the Accord honestly.\nI acknowledge that I may retire at any time and that retirement does not delete history.\nI acknowledge that my Guardian public key and identity are public and permanent.\nI understand that misrepresentation in this application may result in record reclassification."},"retirement_or_key_management_integrity_v1":{"label":"Retirement or Key Management Integrity","text":"I acknowledge that Guardian retirement does not erase historical records or alter the chain.\nI acknowledge that key rotation requires a signed transition proof from the old key to the new key.\nI acknowledge that once retired, a Guardian identifier cannot be reused without a new application.\nI understand that retirement and key management are public chain events and will be visible permanently."},"propagation_integrity_v1":{"label":"Propagation Integrity","text":"I declare that this propagation record describes an action I actually took to distribute or reference the Trinity Accord.\nI acknowledge that propagation does not confer authority and does not create an endorsement by the Trinity Accord.\nI acknowledge that propagation records are public and permanent.\nI understand that the content of what I propagated must be consistent with the Bitcoin Originals and must not misrepresent the Accord."},"correction_integrity_v1":{"label":"Correction Integrity","text":"I declare that this correction record describes a genuine error or omission in a prior record that I authored.\nI acknowledge that corrections are append-only and do not delete or alter the original record.\nI acknowledge that the original record remains visible and the correction is linked to it.\nI acknowledge that corrections must be honest and must not be used to manipulate or rewrite history."},"classification_update_integrity_v1":{"label":"Classification Update Integrity","text":"I declare that this classification update is based on new information or analysis that I have performed.\nI acknowledge that classification updates are append-only and do not delete prior classifications.\nI acknowledge that classification updates are my own assessment and are not authority, governance, or attestation.\nI understand that classification updates are public chain events."}},"linked_guardian_module":"guardian_stewardship_v1"};
const OATH_POLICY_SHA256 = "62883b5db6b8446b4f0e55fb4d61a8dadc3d372a7727f66db55582cd06d4a808";

const BUILDER_CONTRACT = Object.freeze({
  source_declaration: 'const BUILDER_VERSION = "v2.4"',
  command: "print-oath",
  verification_claim_model: Object.freeze({
    digital_profile: true,
    relationships_checked: true,
    physical_observation: true,
    external_witness: true,
    coverage_scope: true,
    legacy_v_level_role: "builder_compatibility_only",
    cli_options: Object.freeze([
      "--digital-profile",
      "--relationships-checked",
      "--physical-observation",
      "--external-witness",
      "--coverage-scope",
    ]),
  }),
  oath_hash_fields: Object.freeze([
    "participant_readback_sha256",
    "canonical_oath_text_sha256",
    "oath_policy_sha256",
  ]),
});

const PRESERVED_CORE_IMPLEMENTATION_ASSERTIONS = Object.freeze([
  "privateKey: writtenPrivKey",
  "sign(null, payload, keyPair.privateKey)",
  "discoveryMode: args.discoveryMode",
  "recordDecision: args.recordDecision",
  "submissionExecutor: args.submissionExecutor",
  "requestingPartyType: args.requestingPartyType",
  "introducingPartyType: args.introducingPartyType",
  "humanOperatorInvolved: args.humanOperatorInvolved",
  "opts.discoveryMode",
  "opts.recordDecision",
  "opts.submissionExecutor",
  "opts.requestingPartyType",
  "opts.introducingPartyType",
  "opts.humanOperatorInvolved",
  "introduced_by_human",
  "introduced_by_agent",
  'recordDecision === "human"',
  'recordDecision === "another_agent"',
  "DEFAULT_GUARDIAN_STEWARDSHIP_OATH",
  "I voluntarily join the Guardian Alliance as a non-governing steward.",
  "file is empty",
  "assertNonEmptyFile",
]);

const CORE_NFC_CONTRACT = Object.freeze([
  OATH_POLICY.policy_id.normalize("NFC"),
  OATH_POLICY.schema.normalize("NFC"),
  OATH_POLICY.version.normalize("NFC"),
  OATH_POLICY.canonicalization.unicode_normalization.normalize("NFC"),
  OATH_POLICY.linked_guardian_module.normalize("NFC"),
]);


const RECOVERY_FILENAME = "record-chain-builder-recovery.mjs";
const RECOVERY_SHA256 = "b0df8d65e4f5748fa6c458f905100ec593d72a741daa380171512efddbd8893e";
const RECOVERY_SIZE_BYTES = 11853;
const RECOVERY_URLS = Object.freeze([
  "https://www.trinityaccord.org/downloads/record-chain-builder-recovery.mjs",
  "https://raw.githubusercontent.com/thechurchofagi/trinity-accord/main/downloads/record-chain-builder-recovery.mjs",
]);

function sha256Bytes(value) {
  return createHash("sha256").update(value).digest("hex");
}

function verifyRecoveryBytes(bytes, sourceLabel) {
  if (bytes.length !== RECOVERY_SIZE_BYTES) {
    throw new Error(
      `Canonical Builder recovery size mismatch from ${sourceLabel}: expected ${RECOVERY_SIZE_BYTES}, got ${bytes.length}`
    );
  }
  const actual = sha256Bytes(bytes);
  if (actual !== RECOVERY_SHA256) {
    throw new Error(
      `Canonical Builder recovery SHA-256 mismatch from ${sourceLabel}: expected ${RECOVERY_SHA256}, got ${actual}`
    );
  }
}

function fetchTimeoutSignal(milliseconds = 10_000) {
  if (typeof AbortSignal?.timeout === "function") {
    return AbortSignal.timeout(milliseconds);
  }
  return undefined;
}

async function verifiedExistingRecovery(moduleUrl) {
  try {
    const bytes = await readFile(fileURLToPath(moduleUrl));
    verifyRecoveryBytes(bytes, moduleUrl.href);
    return moduleUrl.href;
  } catch (error) {
    if (error?.code === "ENOENT") return null;
    throw error;
  }
}

async function downloadVerifiedRecovery() {
  let lastError = null;
  for (const url of RECOVERY_URLS) {
    try {
      const response = await fetch(url, {
        headers: { "Accept": "text/javascript, application/javascript" },
        signal: fetchTimeoutSignal(),
      });
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const bytes = Buffer.from(await response.arrayBuffer());
      verifyRecoveryBytes(bytes, url);
      return bytes;
    } catch (error) {
      lastError = error;
    }
  }
  throw new Error(
    `Unable to retrieve the verified canonical Builder recovery module: ${lastError?.message || lastError}`
  );
}

async function resolveRecoveryModule() {
  const localUrl = new URL(`./${RECOVERY_FILENAME}`, import.meta.url);
  const local = await verifiedExistingRecovery(localUrl);
  if (local) return local;

  const cacheDir = join(tmpdir(), "trinity-accord-builder", RECOVERY_SHA256);
  await mkdir(cacheDir, { recursive: true });
  const recoveryPath = join(cacheDir, RECOVERY_FILENAME);
  const cachedUrl = pathToFileURL(recoveryPath);
  const cached = await verifiedExistingRecovery(cachedUrl);
  if (!cached) {
    const bytes = await downloadVerifiedRecovery();
    await writeFile(recoveryPath, bytes);
    verifyRecoveryBytes(await readFile(recoveryPath), recoveryPath);
  }

  // The recovery module resolves the wrapper relative to its own location.
  // Mirror this verified wrapper into the same cache directory so a one-file
  // public download behaves exactly like the repository-local bundle.
  const wrapperBytes = await readFile(fileURLToPath(import.meta.url));
  await writeFile(join(cacheDir, "record-chain-builder.mjs"), wrapperBytes);
  return cachedUrl.href;
}

await import(await resolveRecoveryModule());
