DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path foray-reference/foray-protocol-deep-read-v0_1.html
Loomworks · FORAY Reference

FORAY Protocol Deep-Read — v0.1

Version: 0.1
Date: 2026-05-24
Produced by: Claude Code, against the canonical FORAY documentation in /Users/dunin7/foray-kaspathon at commit 18727b4
Audience: The Operator, and Claude.ai (which cannot reach the FORAY repos directly).

What this document is for

The primer summarised FORAY at the protocol level. It was high-quality but it summarised; it didn't enumerate. Claude.ai noticed the gap and asked Claude Code to fill it by reading the canonical material directly. This document is that fill-in: a focused read of seven specific things that the primer named but didn't reproduce.

Read alongside the primer, not as a replacement for it. Where the primer makes a load-bearing claim, this document either backs it up with the exact wording from the source, or flags that the source is silent on the question.

What's in here
  1. The six attestation validation rules (ATT-001 through ATT-006) — verbatim, with interpretations
  2. Audit data extension and the four audit profiles — what's documented and what isn't
  3. A worked end-to-end FORAY transaction (the Manuka honey example, in full)
  4. The Type 1 / Type 2 trust model — what attestations prove and what they don't
  5. The FORAY anchor service — and the honest admission of what's undocumented
  6. Transaction-boundary edge cases — reversal, system-generated, date-range, others
  7. Spec drift on attestation framing — three positions in the canonical material

Source material actually read

Verifying the deep-read is grounded in concrete files rather than memory:

FileSizePurpose in this deep-read
FORAY_Protocol_v4_1_Specification.md1,561 lines / 48 KBAttestation rules and framing
guides/FORAY_Attestation_Trust_Model.md304 linesType 1 / Type 2 trust model
docs/DUNIN7_FORAY_Schema_Reference.md621 linesSubmission envelope, edge cases, framing
examples/manuka-honey-provenance-v41.json330 linesThe worked transaction
integration-guide.html, README.md~46 KB totalAnchor service operational description
foray-api-server.js, quickbooks-adapter.js, proxy-server.js~62 KB totalCross-checks for the anchor service investigation
Two documents the kickoff named but the repository does not contain

FORAY_Protocol_v4_0_Specification.md — referenced for the Audit Data Extension. Not in repo. The v4.1 spec references audit_data but does not define it structurally.

FORAY_Transaction_Boundary_Analysis.md — referenced for boundary edge cases. Not in repo. The closest substitute is the Schema Reference's §5, §6, §7, §10.3, §11, §12 — treated as the canonical boundary material since it's what exists.


Section 1 — Attestation validation rules (ATT-001 through ATT-006)

The v4.1 spec presents the six rules as a single short table — no per-rule examples, no error severity, no remediation guidance. The rules are reproduced verbatim in the left column below; the right column gives a plain-English interpretation of what each rule means in practice.

Rule (verbatim from §9.6)What it means in practice
ATT-001: id must be unique and begin with ATT_ Two constraints in one. Identifier uniqueness across the attestations array, plus a string-prefix convention. The other component types follow analogous prefixes (ARR_, ACC_, ANT_, ACT_) — observable in worked examples but not codified for those types in numbered rules.
ATT-002: subject_refs must reference valid component IDs Reference resolution against IDs present in the same transaction. subject_refs is how an attestation reaches into the DAG; it can point to ARR, ACC, ANT, ACT — or to another attestation (forming chains, governed by ATT-006).
ATT-003: attestation_date must not be in the future A temporal sanity rule. There's no companion rule for the date being too far in the past — old attestations are valid.
ATT-004: If validity_period.end < current date, outcome should be expired The only rule using "should" rather than "must." The spec doesn't say what happens if a stale validity-period attestation has a non-expired outcome — likely a warning rather than a blocking error. Treat as a non-blocking advisory absent clarifying text.
ATT-005: attestor_hash must be valid SHA-256 Constrains the format of the hash field but not its derivation. The spec does not say what attestor identity gets hashed — legal entity name? credentialed identity? per-attestor key? Worked examples use descriptive natural-language labels (sha256:umf_assoc_nz_2026...) which are illustrative truncations, not real hashes.
ATT-006: Circular attestation references are prohibited Attestation chains are explicitly supported (A → B is fine; lab → certifier → regulator is the canonical case). But A → B → A is rejected. The spec does not say at what depth circularity detection runs, nor whether the rule applies across transactions or only within one transaction's attestations array.
What the spec doesn't tell you

No per-rule examples. No error severity, no error codes, no remediation guidance. The spec calls these "Validation Rules" without classification. Practical advice: treat all six as validation errors by default, with the noted caveat that ATT-004 uses should.


Section 2 — Audit data extension and the four audit profiles

The kickoff asked for the structural definition of audit_data from the v4.0 spec. The v4.0 spec is not in the repository. What is available falls well short of what the kickoff anticipated, and it's important to be honest about the gap.

What's present in the v4.1 spec

The top-level schema (§2.1) defines only the anchor field that points at audit_data — not the audit_data structure itself:

"audit_data_anchor": {
  "audit_data_hash": "sha256:...",
  "audit_profile": "standard|dcaa_full|big4|minimal",
  "storage_locations": [...]
}

The four audit_profile values — standard, dcaa_full, big4, minimal — are mentioned by name. §2.2 marks audit_data_anchor itself as optional.

What the v4.1 spec defers

In §§4.2, 4.3, 4.4, 4.5 — the four worked examples (cash sale, depreciation, payment, RMBS) — the audit_data block consistently appears as:

"audit_data": {
  "...see audit_data specification..."
}

This placeholder appears in all four worked examples in the spec body. There is no "audit_data specification" document in the repository to follow that pointer to.

What the worked example shows

In examples/manuka-honey-provenance-v41.json, audit_data_anchor carries only the three documented fields, and the actual audit_data payload is not inline:

"audit_data_anchor": {
  "audit_data_hash": "sha256:manuka_audit_data...",
  "audit_profile": "standard",
  "storage_locations": ["s3://foray-audit/2026/Q1/PROV_2026_Q1_MANUKA_HONEY_BATCH_001"]
}

storage_locations points to off-chain storage (here, an S3 prefix). None of the 12 example files inline an audit_data block; all use the anchor-only pattern.

The honest gap

The four audit-profile names appear by name in the v4.1 spec and the HTML specification. Nowhere in the local repository is it stated what each profile contains, what the field-level differences are, whether minimal is a strict subset of standard, or whether profiles can be mixed within a transaction. Practical questions about audit_data shape that the substrate review may have to answer cannot be sourced from this repo. The v4.0 spec — which the kickoff names — is the most likely place for the missing structure. It is not in this clone.

What the spec does say about layering

The architecture has three layers and the deep read captures them succinctly: (a) foray_core is on-chain — the minimum to identify the transaction; (b) the component arrays carry the deal structure and are hashed individually into component_hashes; (c) audit_data is off-chain detail referenced by a single hash in audit_data_anchor. The primer's "hashed separately" claim is consistent with this layering. But the spec doesn't enumerate exactly what's in audit_data at each profile level.

Things asked for that aren't in the source


Section 3 — A worked transaction (Manuka honey, in full)

The Manuka honey example is the canonical complex v4.1 example. It is a Type 2 (attestation) provenance transaction with all five component arrays populated — ARR×2 + ACC×2 + ANT×2 + ACT×2 + ATT×3 — and explicit dependency / _refs[] chains throughout.

The example is reproduced verbatim below. Before reading the JSON, the things worth noticing as you scan it:

The full JSON

{
  "transaction_id": "PROV_2026_Q1_MANUKA_HONEY_BATCH_001",
  "schema_version": "4.1",
  "timestamp": "2026-01-15T09:00:00Z",

  "foray_core": {
    "entity": "Waikato Apiaries Ltd",
    "entity_hash": "sha256:wal_2026_abc123...",
    "transaction_type": "product_provenance",
    "total_value": 185000.00,
    "currency": "NZD",
    "status": "completed",
    "compliance_flags": ["MPI_Export_Certified", "UMF_Licensed", "ISO_22000"]
  },

  "component_hashes": {
    "arrangements": "sha256:arr_manuka_a1b2c3...",
    "accruals": "sha256:acc_manuka_d4e5f6...",
    "anticipations": "sha256:ant_manuka_g7h8i9...",
    "actions": "sha256:act_manuka_j0k1l2...",
    "attestations": "sha256:att_manuka_m3n4o5..."
  },

  "arrangements": [
    {
      "id": "ARR_UMF_LICENSE_2026",
      "foray_core": {
        "type": "origin_certification",
        "effective_date": "2026-01-01T00:00:00Z",
        "parties": [
          { "role": "producer", "name": "Waikato Apiaries Ltd", "jurisdiction": "NZ" },
          { "role": "certifier", "name": "UMF Honey Association", "jurisdiction": "NZ" },
          { "role": "laboratory", "name": "Analytica Laboratories", "jurisdiction": "NZ" }
        ],
        "description": "UMF certification license for authentic New Zealand Manuka Honey",
        "total_value": 185000.00,
        "currency": "NZD",
        "terms": {
          "certification_type": "UMF_Licensed_Producer",
          "license_number": "UMF-2026-0847",
          "valid_until": "2026-12-31",
          "annual_audit_required": true
        },
        "dependencies": []
      }
    },
    {
      "id": "ARR_BATCH_DEFINITION_2026_001",
      "foray_core": {
        "type": "production_batch",
        "effective_date": "2026-01-10T00:00:00Z",
        "parties": [
          { "role": "producer", "name": "Waikato Apiaries Ltd", "jurisdiction": "NZ" }
        ],
        "description": "Production batch MH-2026-001 - 500kg UMF 15+ Manuka Honey",
        "total_value": 185000.00,
        "currency": "NZD",
        "terms": {
          "batch_id": "MH-2026-001",
          "weight_kg": 500,
          "jar_count": 1000,
          "jar_size_g": 500,
          "harvest_date": "2025-12-20",
          "extraction_date": "2025-12-22",
          "apiary_region": "Waikato, North Island",
          "hive_source_ids": ["HIVE-W-042", "HIVE-W-043", "HIVE-W-044"]
        },
        "dependencies": ["ARR_UMF_LICENSE_2026"]
      }
    }
  ],

  "accruals": [
    {
      "id": "ACC_LABORATORY_ANALYSIS_001",
      "foray_core": {
        "arrangement_refs": ["ARR_BATCH_DEFINITION_2026_001", "ARR_UMF_LICENSE_2026"],
        "type": "product_analysis",
        "description": "Laboratory analysis results for batch MH-2026-001",
        "computation_method": "Valuation",
        "formula_id": "sha256:umf_analysis_protocol_v2...",
        "inputs": {
          "sample_id": "SAMPLE-MH-2026-001-A",
          "analysis_date": "2026-01-12",
          "methodology": "UMF_Grading_Standard_v4"
        },
        "output": 185000.00,
        "currency": "NZD",
        "analysis_results": {
          "mgo_mg_kg": 514,
          "dha_mg_kg": 1250,
          "hmo_mg_kg": 42,
          "leptosperin_mg_kg": 185,
          "umf_rating": 15,
          "umf_grade": "UMF 15+",
          "npa_equivalent": 15.2,
          "moisture_percent": 17.8,
          "hmf_mg_kg": 12,
          "diastase_number": 18,
          "authenticity_markers": {
            "c4_sugar_percent": 0.8,
            "pollen_analysis": "Leptospermum scoparium dominant",
            "dna_verified": true
          },
          "classification": "Authentic Monofloral Manuka",
          "grade_confirmed": true
        },
        "dependencies": ["ARR_BATCH_DEFINITION_2026_001"]
      }
    },
    {
      "id": "ACC_BATCH_VALUATION_001",
      "foray_core": {
        "arrangement_refs": ["ARR_BATCH_DEFINITION_2026_001"],
        "type": "inventory_valuation",
        "description": "Batch valuation at export wholesale price",
        "computation_method": "Calculated",
        "formula_id": "sha256:valuation_weight_x_grade_price...",
        "inputs": {
          "weight_kg": 500,
          "umf_grade": "UMF 15+",
          "price_per_kg_nzd": 370.00
        },
        "output": 185000.00,
        "currency": "NZD",
        "dependencies": ["ARR_BATCH_DEFINITION_2026_001"]
      }
    }
  ],

  "anticipations": [
    {
      "id": "ANT_EXPORT_DISTRIBUTION_2026_001",
      "foray_core": {
        "accrual_refs": ["ACC_BATCH_VALUATION_001"],
        "arrangement_refs": ["ARR_BATCH_DEFINITION_2026_001"],
        "type": "scheduled_distribution",
        "description": "Expected export distribution to international partners",
        "expected_amount": 185000.00,
        "currency": "NZD",
        "expected_date": "2026-02-15",
        "probability_factor": 0.95,
        "distribution_plan": {
          "export_markets": ["China", "UK", "USA", "Japan"],
          "jars_per_market": { "china": 400, "uk": 250, "usa": 200, "japan": 150 },
          "mpi_export_certificate_required": true
        },
        "dependencies": ["ACC_BATCH_VALUATION_001"]
      }
    },
    {
      "id": "ANT_SHELF_LIFE_WINDOW_001",
      "foray_core": {
        "accrual_refs": ["ACC_LABORATORY_ANALYSIS_001"],
        "arrangement_refs": ["ARR_BATCH_DEFINITION_2026_001"],
        "type": "quality_validity_window",
        "description": "Expected shelf life based on analysis results",
        "expected_amount": 0,
        "currency": "NZD",
        "expected_date": "2031-01-15",
        "probability_factor": 0.95,
        "quality_parameters": {
          "best_before_date": "2031-01-15",
          "optimal_storage_temp_c": "below 25",
          "light_exposure": "protected",
          "note": "Honey does not spoil; UMF activity may decrease over time"
        },
        "dependencies": ["ACC_LABORATORY_ANALYSIS_001"]
      }
    }
  ],

  "actions": [
    {
      "id": "ACT_JARRING_COMPLETE_001",
      "foray_core": {
        "anticipation_refs": ["ANT_EXPORT_DISTRIBUTION_2026_001"],
        "accrual_refs": ["ACC_LABORATORY_ANALYSIS_001", "ACC_BATCH_VALUATION_001"],
        "arrangement_refs": ["ARR_BATCH_DEFINITION_2026_001"],
        "type": "production_completion",
        "description": "Jarring completed for batch MH-2026-001",
        "amount_settled": 185000.00,
        "currency": "NZD",
        "settlement_date": "2026-01-15T09:00:00Z",
        "settlement_status": "completed",
        "payment_method": "other",
        "counterparty": "Internal",
        "production_details": {
          "jars_produced": 1000,
          "jars_passed_qc": 998,
          "jars_rejected": 2,
          "rejection_rate_percent": 0.2,
          "tamper_seal_applied": true,
          "umf_label_applied": true
        },
        "dependencies": ["ANT_EXPORT_DISTRIBUTION_2026_001"]
      }
    },
    {
      "id": "ACT_UMF_CERTIFICATION_ISSUED_001",
      "foray_core": {
        "anticipation_refs": [],
        "accrual_refs": ["ACC_LABORATORY_ANALYSIS_001"],
        "arrangement_refs": ["ARR_UMF_LICENSE_2026", "ARR_BATCH_DEFINITION_2026_001"],
        "type": "certification_issuance",
        "description": "UMF certificate issued for batch MH-2026-001",
        "amount_settled": 0,
        "currency": "NZD",
        "settlement_date": "2026-01-14T14:00:00Z",
        "settlement_status": "completed",
        "payment_method": "other",
        "counterparty": "UMF Honey Association",
        "certificate_details": {
          "certificate_number": "UMF-BATCH-2026-00847",
          "issued_by": "UMF Honey Association",
          "valid_for_batch": "MH-2026-001",
          "umf_grade_certified": "UMF 15+",
          "mgo_verified_mg_kg": 514,
          "qr_verification_url": "https://umf.org.nz/verify/UMF-BATCH-2026-00847"
        },
        "dependencies": ["ACC_LABORATORY_ANALYSIS_001"]
      }
    }
  ],

  "attestations": [
    {
      "id": "ATT_LAB_ANALYSIS_001",
      "foray_core": {
        "attestor": "Analytica Laboratories",
        "attestor_hash": "sha256:analytica_nz_2026...",
        "attestor_type": "laboratory",
        "attestor_credentials": ["IANZ_Accredited", "MPI_Recognized", "ISO_17025"],
        "subject_refs": ["ACC_LABORATORY_ANALYSIS_001"],
        "attestation_type": "analysis",
        "attestation_date": "2026-01-12T16:30:00Z",
        "validity_period": { "start": "2026-01-12", "end": "2027-01-12" },
        "outcome": "certified",
        "evidence_hash": "sha256:lab_report_mh2026001...",
        "evidence_location": "off-chain",
        "analysis_summary": {
          "mgo_result_mg_kg": 514,
          "leptosperin_result_mg_kg": 185,
          "dna_verified": true,
          "methodology": "UMF_Grading_Standard_v4"
        },
        "dependencies": []
      }
    },
    {
      "id": "ATT_UMF_CERTIFICATION_001",
      "foray_core": {
        "attestor": "UMF Honey Association",
        "attestor_hash": "sha256:umf_assoc_nz_2026...",
        "attestor_type": "certification_body",
        "attestor_credentials": ["NZ_Govt_Recognized", "Trademark_Owner_UMF", "MPI_Partner"],
        "subject_refs": ["ARR_UMF_LICENSE_2026", "ARR_BATCH_DEFINITION_2026_001", "ATT_LAB_ANALYSIS_001"],
        "attestation_type": "certification",
        "attestation_date": "2026-01-14T14:00:00Z",
        "validity_period": { "start": "2026-01-14", "end": "2026-12-31" },
        "outcome": "certified",
        "evidence_hash": "sha256:umf_certificate_mh2026001...",
        "evidence_location": "off-chain",
        "certificate_details": {
          "certificate_number": "UMF-BATCH-2026-00847",
          "umf_grade": "UMF 15+",
          "qr_verification_url": "https://umf.org.nz/verify/UMF-BATCH-2026-00847"
        },
        "dependencies": ["ATT_LAB_ANALYSIS_001"]
      }
    },
    {
      "id": "ATT_MPI_EXPORT_001",
      "foray_core": {
        "attestor": "Ministry for Primary Industries",
        "attestor_hash": "sha256:mpi_nz_govt_2026...",
        "attestor_type": "regulator",
        "attestor_credentials": ["NZ_Government_Authority", "Food_Safety_Regulator", "Export_Certification_Authority"],
        "subject_refs": ["ARR_BATCH_DEFINITION_2026_001", "ATT_UMF_CERTIFICATION_001"],
        "attestation_type": "approval",
        "attestation_date": "2026-01-15T08:00:00Z",
        "validity_period": { "start": "2026-01-15", "end": "2026-07-15" },
        "outcome": "approved",
        "evidence_hash": "sha256:mpi_export_cert_mh2026001...",
        "evidence_location": "off-chain",
        "export_details": {
          "export_certificate_number": "MPI-EXP-2026-NZ-00847",
          "approved_markets": ["China", "UK", "USA", "Japan"],
          "health_certificate_included": true
        },
        "dependencies": ["ATT_UMF_CERTIFICATION_001"]
      }
    }
  ],

  "merkle_root": "sha256:manuka_batch_001_merkle_root...",

  "blockchain_anchor": {
    "kaspa_tx_id": "kaspa:qr_manuka_batch_001...",
    "block_height": 2950000,
    "confirmation_time_ms": 1100,
    "anchored_at": "2026-01-15T09:00:02Z"
  },

  "audit_data_anchor": {
    "audit_data_hash": "sha256:manuka_audit_data...",
    "audit_profile": "standard",
    "storage_locations": ["s3://foray-audit/2026/Q1/PROV_2026_Q1_MANUKA_HONEY_BATCH_001"]
  },

  "privacy_metadata": {
    "formulas_obfuscated": 2,
    "instance_pools": 3,
    "attack_complexity": "2^96 operations"
  }
}

Commentary — how the references compose, where attestations attach, what the hashes commit to

The reference structure forms a directed acyclic graph. Two distinct reference fabrics run through this transaction.

The intra-DAG _refs[] fabric. Every component except Arrangements carries arrangement_refs[], every component below Accruals carries accrual_refs[], every component below Anticipations carries anticipation_refs[]. These are upstream references — an Action declares which Anticipations it resolves, which Accruals it satisfies, and which Arrangements authorised it. They form chains visible directly in the JSON: ARR_BATCH_DEFINITION depends on ARR_UMF_LICENSE; the lab-analysis Accrual references both arrangements; the jarring Action references one Anticipation, two Accruals, and one Arrangement — a full chain through every layer. This is the "DAG" the primer names.

The attestation fabric — structurally separate. Attestations do not appear in any _refs[] array on any component. They reach into the DAG via subject_refs[] on the attestation itself — a one-way pointer. Three patterns of attachment in this example: ATT_LAB_ANALYSIS_001 attaches to one Accrual; ATT_UMF_CERTIFICATION_001 attaches to two Arrangements and one other attestation (the chain primitive); ATT_MPI_EXPORT_001 attaches to one Arrangement and one prior attestation.

The structural reason "cross-cutting" is the right word

The DAG components have no knowledge of which attestations point at them. The reference always goes from the attestation into the subject, never back. This one-way arrow is what makes attestation a sibling structure, not a fifth peer in the DAG.

What the hashes commit to. Four hash-bearing fields commit at different layers:

The Type 2 framing in this example. The transaction is anchored against three independent attestors — laboratory, certification body, regulator — with progressive chain dependency (lab → certifier → regulator). FORAY does not certify that the honey is Manuka. It certifies that the lab analysed it, that UMF certified the lab's analysis, and that MPI approved the certification for export. This is the canonical "attestation transaction" shape: identified parties making sequential claims under specific credentials, with the chain itself anchored.


Section 4 — Type 1 / Type 2 trust model

The trust-model document is the most explicit place in the FORAY material about what attestations can and cannot prove. It's worth reproducing the key passages verbatim because the exact wording matters.

The distinction

"FORAY transactions fall into two broad categories with different trust characteristics."

Type 1: System-of-Record Transactions — "These transactions record events within authoritative business systems where the system itself is the source of truth."

"Examples: ERP journal entries (SAP, Oracle, QuickBooks), Bank payment confirmations, Manufacturing work orders, Payroll processing."

"Trust Model: The ERP/financial system is authoritative. When QuickBooks records a payment, the payment occurred. FORAY anchors this fact with external, tamper-evident proof."

"FORAY Value: Proves the record existed at a point in time and hasn't been altered — even by system administrators."

Type 2: Attestation Transactions — "These transactions record claims about external reality made by identified parties."

"Examples: Product provenance (Manuka honey origin, watch authenticity), Laboratory certifications (spectroscopic analysis), Third-party inspections, Compliance attestations."

"Trust Model: FORAY anchors a chain of attestations, not independent truth. The value depends entirely on trusting the attestors."

"FORAY Value: Creates a tamper-evident record of who claimed what, when — enabling accountability and dispute resolution."

How a transaction's type is determined

An important nuance: Type 1 / Type 2 is not a typed field on the transaction. The categorisation is imposed by the framing document, not by the schema. The distinction is inferred from structure:

There is no transaction_kind or trust_model field in foray_core. The Type 1 / Type 2 label is editorial categorisation, not a structural feature of the schema. The v4.1 spec's "When to Use Attestations" table is a usage guide, not a structural constraint — a product-provenance transaction can in principle ship without attestations (lossy, but valid); a cash sale can in principle ship with attestations (over-the-top, but valid).

What attestations prove

From §9.7 of the v4.1 spec, reproduced exactly:

What Attestations Prove:

  • A specific party made a specific claim at a specific time
  • The claim has not been altered since anchoring
  • The attestor's credentials are recorded

From the trust-model document, the equivalent framing:

  • Temporal proof — A specific claim was recorded at a specific point in time
  • Integrity proof — The recorded data has not been altered since anchoring
  • Consistency proof — Multiple parties can verify they hold identical records
  • Sequence proof — Events occurred in a verifiable order

What attestations do NOT prove

From v4.1 spec §9.7, reproduced exactly:

What Attestations Do NOT Prove:

  • The claim is true
  • The attestor is competent
  • Physical reality matches the digital record

The trust-model document expands this into a four-row explanation, reproduced exactly:

LimitationExplanation
Truth of claimsFORAY anchors assertions, not facts — if a party claims "this honey is authentic Manuka from New Zealand," FORAY proves they made that claim, not that it's true
Physical realityDigital records cannot independently verify physical world states
Attestor competenceA laboratory's certification is only as reliable as the laboratory
Data accuracy at source"Garbage in, garbage out" — FORAY trusts validated source system data
The key sentence — reproduced exactly

This is not a weakness to hide — it is a boundary to understand. Every audit system shares these limitations. FORAY's contribution is making the recorded claims tamper-evident and the claimants accountable.

The closing summary table (verbatim)

Transaction TypeSource of TruthFORAY ProvesTrust Assumption
System-of-RecordERP/Financial SystemRecord integrity + timestampSystem is authoritative
AttestationIdentified PartiesClaims were made + timestampAttestors are trustworthy

Substrate implications

The trust-model document does not directly answer "does the substrate need a Type 1 / Type 2 discriminator?" Reading the protocol material:

Honest reading: the trust model is editorial categorisation, not a structural feature of the schema. A substrate can implement a discriminator if it makes substrate-side queries cleaner; doing so is a substrate decision, not a protocol mandate.


Section 5 — The FORAY anchor service

The honest finding

The "FORAY anchor service" as a separately defined operational interface is not documented in the local repository as an HTTP API, library, or service contract. There is enough material to know the conceptual shape; there is not enough to know the wire interface.

What the repository does contain

  1. The protocol description — what anchoring conceptually means.
  2. Per-source adapters (quickbooks-adapter.js, salesforce-adapter.js) that call into an ForaySDK module.
  3. The submission envelope in the Schema Reference — the closest thing to a service-interface contract.

The SDK module is referenced but absent

quickbooks-adapter.js:24 reads:

const ForaySDK = require('./foray-sdk');

— and the file foray-sdk.js is not in the repository. The adapter calls into this.sdk.anchorToBlockchain(transaction) (line 309, 389, 457 of quickbooks-adapter.js) and this.sdk.createArrangement(...) etc. for component construction. These are the only points where the calling code touches the anchor surface.

The two API servers in this repo are not the anchor service. foray-api-server.js exposes three endpoints — /api/generate-foray, /api/analyze-business, /api/describe-transaction. All three are Claude-powered demo endpoints for constructing FORAY JSON from natural language, not for anchoring it. proxy-server.js is a CORS proxy. Neither submits to a blockchain.

What can be reconstructed

From the integration guide:

"The universal FORAY integration pattern follows five steps, regardless of source system:

  1. Extract — Pull transaction data from ERP via API, webhook, or scheduled export
  2. Transform — Map to FORAY's 4-component model
  3. Hash — Generate component hashes, compute merkle root, apply privacy obfuscation
  4. Anchor — Submit merkle root to Kaspa blockchain
  5. Store — Archive full transaction JSON with blockchain anchor reference"

The "service" is functionally: a process that takes a constructed FORAY JSON, computes (or accepts) the merkle root, broadcasts it to Kaspa, and writes back the blockchain_anchor block (kaspa_tx_id, block_height, confirmation_time_ms, anchored_at). The repo does not state whether this runs in-process, out-of-process, or as a hosted service.

The closest thing to a service contract — the Submission Envelope

The Schema Reference §4.1 defines a wrapper that is the most service-interface-like artifact in the repository:

{
  "foray_submission": {
    "schema_version": "1.0",
    "transaction_id": "<SHA-256 hash>",
    "parent_reference": { "type": null, "id": null },
    "submission_type": "",
    "submission_timestamp": "",
    "persistence": [],
    "boundaries": { ... },
    "arrangement": {},
    "accruals": [],
    "anticipations": [],
    "actions": []
  }
}
FieldDescription
submission_type"new" / "amendment" / "component" — first submission for this transaction_id, append-only correction, or addition to an existing transaction
persistence[]Persistence layer declarations (see below)
parent_referenceNull if originating; otherwise points to parent Transaction or Accrual

§4.2 (Persistence Layer Declaration) clarifies the anchor relationship:

"FORAY is persistence-layer agnostic. The submitting party declares which persistence layer or layers to use. FORAY executes the anchoring according to the declaration. The cost is borne by the initiator."

And the required properties for FORAY-compatible persistence:

"Required persistence layer properties for FORAY compatibility: Tamper-evident · Independently verifiable · Append-only · Timestamped · Durable · Accessible"
What this makes architecturally clear
  • FORAY does not own anchoring. The submitter declares the persistence layer.
  • Kaspa is one persistence layer; the Schema Reference also names "kaspa-testnet", "ethereum-mainnet", "hyperledger-fabric", "iso-timestamp-log".
  • Multiple persistence layers may be declared simultaneously.
  • An empty persistence[] array is a valid submission — the transaction is recorded without anchoring, and the absence of anchoring is itself part of the record.

Sync vs async

The repo does not say. The adapter code uses await this.sdk.anchorToBlockchain(transaction) — consistent with synchronous-via-async (the call returns once the blockchain has confirmed). Kaspa's 1-second block times make a synchronous flow feasible (the integration guide names ~2 seconds for "Real-time (immediate anchoring)"). The operating model supports both real-time and batched/micro-batched submission; the choice is per-deployment.

Honest summary

Enough material exists to know the conceptual shape: the submitting system constructs a FORAY envelope with a declared persistence layer, submits it to a layer-specific service (probably via the absent foray-sdk.js), receives a blockchain_anchor block once the persistence layer confirms, and stores the resulting full transaction JSON with the anchor reference.

Not enough material exists to know the exact wire interface — endpoint URL, request body shape, response shape, error codes, auth model. If Loomworks' substrate-side design needs to integrate against an anchor service, the substrate is currently designing against an under-documented surface. The protocol-level abstraction is documented; the operational interface is not. Worth flagging.


Section 6 — Transaction boundary edge cases

The kickoff named FORAY_Transaction_Boundary_Analysis.md as the source for this section. That file is not in the repository. The closest material is in the Schema Reference (2026-03-26): §5 (Asset Behaviour Taxonomy), §6 (Transaction Record Configurations), §7 (Multi-Behaviour Combination Rules), §10.3 (Edge Cases & Unusual Transactions), §11 (Schema Additions from Second Validation Pass), and §12 (Initial Transaction Validation Reference — Set Two). This is the canonical boundary analysis available.

What an "edge case" means in FORAY's terms

The Schema Reference frames edge cases not as anomalies that break the protocol, but as transactions where the asset's behaviour reveals a corner of the four-component model that simpler transactions don't exercise. §5 identifies nine asset behaviour types (extensively investigated; no tenth has emerged).

#BehaviourBoundary feature exercised
1Complete TransferBaseline — ARR + ACC + ANT + ACT, single asset moving one direction
2Conditional TransferTwo competing Anticipations exist simultaneously; one resolves with an Action, the other is retired without generating an Action. The retired Anticipation is the reversal-path artifact.
3TransformationInput asset is consumed, output asset is created — they are related but not the same asset. Two Actions in one Arrangement.
4ConsumptionThe asset ceases to exist with no output. Action records disappearance, not transfer.
5Governing ReferenceThe asset never moves — only its derivative obligation does. The reference asset appears in Accrual Properties, never in an Action.
6CreationThe asset did not exist before the transaction. Brought into existence by the Arrangement itself.
7External DestructionAn external event destroys the primary asset. Destruction Action has no recipient. Cause is observable in record but outside the protocol's scope.
8RightRight-grant and right-exercise are separate transactions linked by lineage. The right's existence is one Arrangement; each exercise is a child transaction.
9Perceived ValueThe valuation oracle is the only source of value. The transaction is real; the value is agreed, not derived.

System-generated transactions

The Schema Reference does not name this term explicitly, but the protocol structurally admits it. The Action component (§2.4) admits two types: Transfer and State Change. State Change is "condition confirmed without asset movement — From Party and To Party are the same party." That's the structural admission of system-generated transactions: the protocol accepts state changes as Actions even when no asset moved. Concrete cases in §10.3: Tooling Setup & Changeover ("First transaction producing no transferable output. Action is a state change") and API Call Billing ("Highest-volume type mapped — potentially billions of child Actions against single Arrangement").

Date-range transactions vs point-in-time

The Schema Reference doesn't use the phrase "date-range transaction" but the protocol supports non-point-in-time semantics in several ways:

The protocol does not require timestamps to be point-in-time; ranges and distributions are first-class.

Reversal patterns — three distinct treatments

Reversal as a retired Anticipation (Conditional Transfer, §5):

"Two possible outcomes exist simultaneously — completion or reversal."

"Action: Resolves to one path only. The other Anticipation is retired without generating an Action."

Reversal as an Accrual that reverses direction (§12.5 Government and Public Sector):

"Tax Payment & Refund Cycle | Formula-based Accrual | Accrual obligation can reverse direction between parties."

"Government Grant with Clawback | Conditional Transfer | Clawback is conditional transfer in reverse — asset already transferred may return."

Reversal as a non-monotonic Accrual (§10.3 Carried Interest):

"Accrual is non-monotonic — reverses and rebuilds. Anticipation is awareness until liquidity event."

The protocol expresses reversal through the component lifecycle, not through a reversed flag on the transaction. There is a status: "reversed" value on the top-level envelope, but the Schema Reference's framing is that reversal is a structural lifecycle event, not a status flip — the underlying components carry the reversal mechanics.

The Amendment Protocol — the most precise boundary statement

§4.4 of the Schema Reference is the most precise statement of the substrate-relevant boundary:

"Amendments are append-only. The original record is immutable. Every amendment is a new anchored record that references the original transaction_id. The full history — original plus all amendments — is always visible. Deletion is structurally impossible."

{
  "submission_type": "amendment",
  "transaction_id": "<hash of this amendment>",
  "parent_reference": { "type": "transaction", "id": "<hash of original transaction>" }
}
"An amendment may add or clarify. It may never delete. Any attempt to submit an amendment that removes a previously anchored field is rejected."
A direct mapping to Loomworks

This boundary is structurally identical to Loomworks' Memory non-erasure rule (R-A28). Both substrates enforce append-only correction. Whatever cleanup Loomworks does to its three current FORAY-shaped tables, the append-only discipline carries through.

Other edge cases worth surfacing

Edge caseBoundary feature
Open source bounty (§12.4)Recipient is genuinely unknown at Arrangement creation. Resolved by an Open Recipient placeholder party type at the Arrangement, with the resolving Action carrying the actual party identifier.
Divorce asset settlement (§12.7)A Directing Authority (court) shapes the Arrangement without participating in asset flows. Added as a party role in the optional schema; appears in the Arrangement only.
Organ procurement chain (§12.3)Time-critical viability window in the Anticipation. Breach is structurally visible as a timestamp anomaly.
Revenue-based financing (§12.7)Self-terminating Accrual — the Accrual carries a cumulative cap value. Ceases when the cap is reached.
Letter of Credit (§10.1)"Attestation gates drawdown but is not mandatory for recording" — attestation is a real-world prerequisite for an Action, but FORAY records the transaction structure regardless. The gating is editorial, not structural.

Substrate implications

The Schema Reference is explicit (§11): all edge-case resolutions land in the optional schema layer, never in the core. Implicit substrate guidance:

The substrate design that absorbs all of this with minimum core impact is the layered one the primer recommends — four polymorphic component tables plus an attestations table plus JSONB for optional schema additions.


Section 7 — Spec drift on Attestation framing

The deep-read surfaces something the primer didn't fully reveal: three positions on Attestation framing exist in the canonical material, not two. The primer named two — the v4.1 spec's framing and the primer's own. The Schema Reference (2026-03-26) holds a third position, mid-way between them. Reproduced verbatim below.

Position 1 — The v4.1 spec (§9.1)

Section header: "9. Attestations Extension (Optional)"

Opening paragraph:

"The Attestations extension provides a fifth component type for transactions requiring third-party validation. This is optional — the core 4A model (Arrangements, Accruals, Anticipations, Actions) remains sufficient for most enterprise transactions."

The default top-level schema in §2.1 has component_hashes with four entries (arrangements, accruals, anticipations, actions). Attestations are added only when used. §9.5 ("Schema Extension") frames this structurally: "When using attestations, add to component_hashes..."

Position 2 — The Schema Reference 2026-03-26 (§2 and §3.1)

Section §2 (Core Schema), opening:

"The core schema consists of four components — Arrangements, Accruals, Anticipations, and Actions (4A). Attestations are available as a FORAY capability but are not part of the core schema. A transaction is complete without them."

But §3.1 (Optional Schema Layer — Attestations) immediately reframes:

"Attestations are the event layer of the FORAY protocol. They are not business transactions. They are claims — made by an identified, credentialed party, at a defined point in time, anchored tamper-evidently alongside the transaction they reference."

"An Attestation establishes: who claimed what, under what credentials, at what time. The tamper-evident anchor makes the claim permanently attributable. Whether the claim was honest, accurate, or complete is outside the boundary of the protocol. A false Attestation is permanently attributable to its author. FORAY does not prevent false claims. It makes them impossible to deny."

"This is the layer where AI agent observability connects to business transaction accountability."

This position is mid-way between Position 1 and Position 3. It holds (a) "not part of the core schema" and (b) "the event layer of the FORAY protocol" — which is a structurally distinct framing from "optional extension."

Position 3 — The primer

From the primer's framing:

"The refined position this primer uses is that FORAY is a five-component model in which Attestation is a native cross-cutting capability, not a downstream extension."

"Four component types (ARR, ACC, ANT, ACT) are peers in the transaction DAG; Attestation (ATT) is a fifth, structurally distinct kind — it does not sit in the DAG, it annotates it."

"This is the deepest structural reason attestation is 'cross-cutting': no DAG component has an attestation_refs[] field. The reference always goes from the attestation into the thing being attested to, never the reverse."

Wording difference vs structural difference

The three positions produce identical JSON output. A v4.1-compliant transaction looks the same whether attestations are "an extension," "the event layer," or "a cross-cutting fifth." The transaction validates against the same rules under all three framings.

The difference is conceptual and substrate-shaping:

The structural one-way reference is the same in all three positions. Every reading of the spec has the same answer: attestations point at components, components do not point at attestations. The structural fact is not contested. What is contested is the conceptual posture (optional / layered / native), which informs substrate-level decisions like whether the attestations table is created in v1 or deferred, whether queries that walk the DAG include attestations by default, and whether the substrate needs a typed attestation_refs[] column on each DAG component (no — because the spec is structurally clear that reference goes the other way).

What changed between v4.1 and the Schema Reference

The v4.1 spec is dated 2026-02-14. The Schema Reference is dated 2026-03-26 — six weeks later. The Schema Reference is the more recent document and shows the conceptual evolution:

The primer goes one further step — past "event layer" to "native cross-cutting capability." That step is unambiguously not in either spec document; it's the primer's synthesis.

Honest reading

The drift is real but uniformly in one direction: each successive document strengthens attestation's structural standing relative to the v4.1 spec. There is no document anywhere in the repo that retracts the v4.1 spec's framing — it remains the canonical wire-format reference — but every document written after it positions attestations as more central than the v4.1 spec's "Extension (Optional)" language suggests.

For substrate design: the primer's framing (native cross-cutting) is the strongest of the three and is consistent with the trajectory of the documentation. A substrate that builds attestations in from v1 — with a peer table and the one-way reference fabric — is consistent with all three positions even though only Position 3 demands it.


Things found beyond the kickoff list

Six observations that don't fit the seven topics but seem load-bearing for substrate work.

1. The Submission Envelope is the most service-interface-like artifact

Schema Reference §4 defines a wrapper around the transaction (foray_submission) with three submission types: new, amendment, component. This is structurally distinct from the v4.1 spec's top-level transaction object. The substrate may want to model this layering: a foray_submissions table records every submission attempt; the foray_transactions row is the materialized result of one or more submissions for a given transaction_id. The three submission types map to substrate operations:

2. The Transaction ID is generated by the submitter, not by FORAY

§4.3 of the Schema Reference:

"transaction_id = SHA-256( namespace + timestamp + entropy )"

"The pre-image is retained by the submitting agent's key management implementation. The submission agent never stores it in plaintext."

Substrate implication: Loomworks either generates the transaction_id (and retains the pre-image for the proving-ownership story) or accepts it from upstream. The substrate cannot synthesize a transaction_id on demand without also synthesizing the namespace/entropy/timestamp triple.

3. Persistence layer is plural and declared per-submission

§4.2 makes explicit that a transaction may be anchored to multiple persistence layers simultaneously:

"Multiple layers may be declared simultaneously. A party that anchors to a credible, independently verifiable layer carries more weight in the execution fidelity model than one that anchors to an unverified private log."

A substrate that stores blockchain_anchor as a single embedded object on the transaction row is structurally one-layer-only. If the protocol is to be honoured, the substrate needs transaction_anchors (plural) — one row per (transaction_id, persistence_layer).

4. Execution fidelity weighting is a substrate-shaping concept the primer doesn't surface

"At transaction inception, a completeness weight is calculated based on the data supplied relative to what the transaction type requires. This weight can be recalculated at any subsequent point as Actions resolve against Anticipations."

The substrate would need to record the inception-time weight, the recomputed weights, and the cumulative party-level weight derived from many transactions. The primer's four-table design doesn't allow for this without an additional layer.

5. AI-agent observability is named as the canonical Attestation use case for FORAY's near future

Schema Reference §3.1:

"This is the layer where AI agent observability connects to business transaction accountability. An AI agent's decision cycle — authorisation check, reasoning, projection, execution — is a sequence of events, not a business transaction. Each event is a claim made by the agent about what it did and why."

Directly relevant to the substrate review: Loomworks itself is an AI-agent-operated substrate. The Schema Reference's framing suggests that Loomworks-as-a-FORAY-system would have its actor decisions (Companion turns, classifier outputs, agent dispatch records) modelled as Attestations against the engagement-as-Transaction. This is a deeper substrate implication than the primer states.

6. The protocol explicitly anticipates multiple deployment models

"The FORAY protocol is deployment-model neutral. Compliant implementations may be self-hosted, embedded in connectors, or offered as commercial services."

"Commercial implementations of the FORAY protocol — including hosted services offered to third parties — require a commercial license from DUNIN7."

For Loomworks: the substrate's relationship to FORAY can be embedded (calls into a local SDK), federated (each tenant declares its own persistence layer), or hosted (Loomworks operates the anchor service). The protocol doesn't pick one.


Things asked for but not found

TopicWhat's missingWhere the answer might live
Audit data extension (Topic 2) Structural definition of audit_data; field-level differences between standard, dcaa_full, big4, minimal; whether profiles can be mixed within a transaction FORAY_Protocol_v4_0_Specification.md (not in this clone); possibly in DUNIN7's private documentation
Anchor service interface (Topic 5) Wire-level interface for anchoring: endpoint URL, request/response shapes, error codes, auth model, sync-vs-async semantics foray-sdk.js (referenced by adapters but not in repo); possibly in DUNIN7's private documentation
Boundary edge cases (Topic 6) FORAY_Transaction_Boundary_Analysis.md as a discrete document Substituted with Schema Reference §5/§6/§7/§10.3/§11/§12, which covers the same conceptual ground organised by asset behaviour
ATT-* validation rule examples The §9.6 table gives statements but no per-rule example of violation or accepted outcome Possibly in test fixtures (not searched exhaustively); no obvious test directory in the repo

The substrate-review work can proceed against everything in the seven topics that was found, with the gaps above flagged. Topics 2 and 5 are the most consequential gaps — Topic 2 because audit-profile differences may shape a substrate-side audit_data table, and Topic 5 because the substrate will need to integrate against some anchor surface whose interface is not documented in the local clone.