Architecture · Modules
The six modules
Six audited governance modules. Every template is a configuration of these six. M1 is the substrate everything else reads.
Templates differ by configuration of the same six modules, not by having different modules. The audit surface is therefore bounded: six modules · parameter ranges · template-specific compositions. This page renders the canonical specification at audit-scoping rigor.
Module index
- M1 — Beneficiary Registry · deep-link
- M2 — Governance · deep-link
- M3 — Economics · deep-link
- M4 — Management Plan Ratification · deep-link
- M5 — Attestation Hooks · deep-link
- M6 — Upgrade Path · deep-link
Every governance vehicle (Tier 1 LLC or Tier 2 DAO) is composed from the same six modules. The differentiator across templates is each module’s configuration, not the modules themselves.
This document is written to smart-contract-audit-scoping rigor. For each module it specifies purpose, Tier 1 implementation (operating-agreement and multi-sig encoding), Tier 2 implementation (state variables, function signatures, events, modifiers), invariants, threat model, per-template variations, external dependencies, and audit-scoping notes. Solidity-style pseudocode is used for concreteness; (framework-agnostic; specific framework choice deferred) — the spec must be implementable on Aragon OSx, Tally, OpenZeppelin Governor, or a custom Solidity codebase. No syntax in this document locks in any of these.
Where a claim turns on legal counsel or jurisdictional law, it is marked (counsel-confirmation required). Where it turns on technology not yet built, (not yet built).
The modules — one-line summary
| Module | Function | Tier 1 implementation | Tier 2 implementation |
|---|---|---|---|
| M1 — Beneficiary Registry | Named participant list with seats, KYC reference, voting weight, distribution share | Operating-agreement membership clauses; KYC records held off-chain by Landseed compliance | Smart-contract registry; KYC reference held off-chain; on-chain seat designation |
| M2 — Governance | Proposal types, voting rules, supermajority thresholds, time delays, FPIC checkpoints, guardian vetoes | Operating-agreement procedures; signed amendments; institutional governance integration | Smart-contract governance with on-chain proposal lifecycle, voting, and execution |
| M3 — Economics | Distribution rules — the benefits model | Operating-agreement distribution clauses; multi-sig executes distributions | Smart-contract distribution module with parameterized splits and cadences |
| M4 — Mgmt Plan Ratification | Versioned forest/invasive/harvest/restoration plans; the LLC/DAO ratifies versions through M2 | Operating-agreement amendment procedure; institutional plan approval | Content-addressed plan ratification; on-chain version tracking |
| M5 — Attestation Hooks | Consumes EC-M-1.1 attestations from receipts; recognizes credit-issuance events | Manual recording in books and records; Landseed compliance verifies | Smart-contract listener for attestations and credit-issuance events; on-chain state updates |
| M6 — Upgrade Path | Module-by-module upgrades with supermajority + time delay + guardian veto | Operating-agreement amendment procedure; standard signature-based amendment | Smart-contract upgrade governance with audit-required new module deployment |
Cross-module dependencies (read once before reading the module sections):
M1 (Registry) ──► M2 (Governance) ──► M4 (Mgmt Plan)
│ │
│ ├──► M3 (Economics) ──► (treasury → wrapper LLC)
│ │ ▲
│ └──► M6 (Upgrade) │
│ │
└──► M5 (Attestation Hooks) ◄──── Registry contract (off-DAO; per `05-interfaces/04`)
│
└──► Buffer pool (Foundation; per `05-interfaces/05`)
M1 is the membership substrate; M2 reads M1 for voting weight; M3 reads M1 for distribution share; M5 is a passive listener that updates state used by M3; M4 is content-addressed state ratified through M2; M6 governs replacement of any module.
M1 — Beneficiary Registry
1. Purpose
The list of named participants and their seats. Permissioned-only — entries added through governance, not market mechanism. M1 is the on-chain (or operating-agreement) source of truth for who has what authority and what economic share in this property’s vehicle. Every other module reads from M1.
2. Tier 1 implementation (Vermont LLC + multi-sig + operating-agreement clauses)
Encoded in operating agreement:
- Named members with legal identity and seat designation (landowner-primary, family member, land-trust-officer, Landseed-steward, etc.)
- Per-decision-class voting weight (use decisions, methodology decisions, treasury actions)
- Distribution share for each member (M3 input)
- Successor-designation clauses (per template; see Successor mechanics below)
- Membership-admission procedure (signature requirements, notice periods)
- Resignation and removal procedure
- KYC representations and warranties
Encoded in multi-sig:
- Signer set corresponding to operating-agreement-named members or their designated wallet operators
- Threshold (typically 2-of-3 for Template A; institutional N-of-M for Template B; composed for F)
- Daily/per-transaction limits aligned with operating-agreement treasury authority
Held off-chain by Landseed compliance:
- KYC vendor records (Persona, Veriff, or Sumsub per
06-risks/04-proposed-resolutions.mdQ10) — (vendor selection counsel-confirmation required) - KYC re-verification calendar (every 5 years, on succession, on material membership change)
- Wallet-to-legal-identity mapping (the on-chain identifier in Tier 2 is opaque; the mapping lives only in compliance records)
3. Tier 2 implementation
(framework-agnostic; specific framework choice deferred — Aragon, Tally Governor, OpenZeppelin Governor, or custom Solidity all acceptable)
3.1 State variables
struct Beneficiary {
address holder; // current wallet (Q9 hybrid custodial/hardware/paper auth)
uint8 class; // SeatClass enum: LANDOWNER, FAMILY, LAND_TRUST,
// COMMUNITY_COUNCIL, CULTURAL_GUARDIAN,
// ELDER, CORPORATE, SOVEREIGN,
// LANDSEED_STEWARD, METHODOLOGY_GUARDIAN
bytes32 kycRefHash; // opaque pointer to off-chain KYC record (no PII on-chain)
uint64 admittedAt; // block.timestamp of admission ratification
uint64 kycExpiresAt; // block.timestamp at which KYC must be re-verified
uint64 successorDeadline; // block.timestamp; 0 if no pending succession
bool active; // false on resignation/death/removal pending succession
}
mapping(uint256 => Beneficiary) internal _registry; // memberId => Beneficiary
mapping(address => uint256) internal _holderToMemberId; // wallet => memberId (0 = none)
mapping(uint256 => mapping(uint8 => uint256)) internal _votingWeight;
// memberId => decisionClass => weight
mapping(uint256 => uint256) internal _distributionShareBps;
// memberId => basis points (sum ≤ 10_000)
bytes32 public successorRoot; // merkle root of pre-declared successor designations
// (used for fast-path replacement; see §3.4)
uint256 public memberCount; // count of active members
uint256 public nextMemberId; // monotonic; never reused
bool public kycRequired; // template-level toggle (always true in current architecture)
uint8 public constant DECISION_USE = 0;
uint8 public constant DECISION_METHODOLOGY = 1;
uint8 public constant DECISION_TREASURY = 2;
uint8 public constant DECISION_CULTURAL = 3; // Template C / F-with-C only
uint8 public constant DECISION_STEWARDSHIP = 4;
uint8 public constant DECISION_UPGRADE = 5;
3.2 Public function signatures
// Admission — gated by M2 governance proposal
function admitMember(
address holder,
uint8 class,
bytes32 kycRefHash,
uint256[6] calldata weightsByDecisionClass,
uint256 distributionShareBps,
bytes calldata governanceProof // proof that an M2 proposal authorized this admission
) external onlyGovernance returns (uint256 memberId);
// Succession — gated by template-specific successor mechanic
function succeedMember(
uint256 memberId,
address newHolder,
bytes32 newKycRefHash,
bytes calldata successorProof // template-dependent; merkle proof, FPIC ratification,
// or institutional-procedure attestation
) external returns (bool);
// Resignation — member-initiated; triggers successor procedure per template
function resign(uint256 memberId) external;
// Removal — gated by M2 supermajority
function removeMember(
uint256 memberId,
bytes calldata governanceProof
) external onlyGovernance;
// KYC refresh — Landseed-compliance-officer-only update of expiration
function refreshKyc(
uint256 memberId,
bytes32 newKycRefHash,
uint64 newExpiry
) external onlyComplianceOfficer;
// Successor root update (pre-declared successor merkle tree, per template)
function setSuccessorRoot(bytes32 newRoot, bytes calldata governanceProof)
external onlyGovernance;
// Read-side
function getBeneficiary(uint256 memberId) external view returns (Beneficiary memory);
function getVotingWeight(uint256 memberId, uint8 decisionClass)
external view returns (uint256);
function getDistributionShareBps(uint256 memberId) external view returns (uint256);
function isActive(uint256 memberId) external view returns (bool);
function totalVotingWeight(uint8 decisionClass) external view returns (uint256);
function totalDistributionShareBps() external view returns (uint256);
3.3 Internal/private functions
function _verifyKycLive(uint256 memberId) internal view;
// reverts if kycExpiresAt < block.timestamp
function _validateWeights(uint256[6] memory weights) internal pure;
// reverts on overflow; reverts on weight > MAX_PER_CLASS
function _validateDistributionShareInvariant() internal view;
// sum of distributionShareBps across active members ≤ 10_000
function _verifySuccessorProof(
uint256 memberId,
address newHolder,
bytes calldata proof,
uint8 templateMechanic
) internal view returns (bool);
3.4 Events
event MemberAdmitted(
uint256 indexed memberId,
address indexed holder,
uint8 indexed class,
bytes32 kycRefHash,
uint256 timestamp
);
event MemberSucceeded(
uint256 indexed memberId,
address indexed oldHolder,
address indexed newHolder,
bytes32 mechanism // hash of successor mechanic + proof
);
event MemberResigned(uint256 indexed memberId, address indexed holder, uint256 timestamp);
event MemberRemoved(uint256 indexed memberId, bytes32 indexed governanceProposalId);
event KycRefreshed(uint256 indexed memberId, bytes32 newKycRefHash, uint64 newExpiry);
event SuccessorRootUpdated(bytes32 oldRoot, bytes32 newRoot);
event WeightUpdated(uint256 indexed memberId, uint8 indexed decisionClass, uint256 weight);
event DistributionShareUpdated(uint256 indexed memberId, uint256 oldBps, uint256 newBps);
3.5 Modifiers and access control
modifier onlyGovernance(); // msg.sender == address(governanceModule)
// AND a current proposal authorizes the call
modifier onlyComplianceOfficer(); // msg.sender ∈ complianceOfficerSet
// (multi-sig of Landseed compliance officers)
modifier onlyHolder(uint256 memberId);
modifier kycCurrent(uint256 memberId);
// reverts if KYC expired
modifier nonTransferableInvariant();
// ensures no path other than succeedMember / removeMember
// changes the holder of a position
4. Invariants
The auditor must verify each of these holds across all reachable contract states:
| # | Invariant |
|---|---|
| M1.1 | Non-transferable: only succeedMember and removeMember can change the holder of an existing memberId. No transfer, no approve, no setHolder external function exists. |
| M1.2 | KYC-gated: any state-changing function on a member’s behalf reverts if _verifyKycLive(memberId) reverts. (Read functions exempt.) |
| M1.3 | Sum-of-shares ≤ 100%: Σ distributionShareBps[i] for active i ≤ 10_000. Enforced atomically on every admitMember, removeMember, resign. |
| M1.4 | Per-class total voting weight is computable and non-zero: totalVotingWeight(class) > 0 for every class active in the deployed configuration. |
| M1.5 | Succession completeness: a memberId in successorDeadline > 0 state cannot vote, propose, or receive distributions until succession resolves. |
| M1.6 | Monotonic memberIds: nextMemberId only increases; memberIds are never reused. |
| M1.7 | No self-admission: admitMember cannot be invoked except via onlyGovernance. The contract itself is not a holder. |
| M1.8 | Wallet uniqueness: at any block, each holder address maps to at most one active memberId. |
| M1.9 | KYC-hash opacity: no on-chain logic interprets kycRefHash semantically; it is treated as an opaque pointer. |
| M1.10 | Successor root consistency: when successorRoot is non-zero, every fast-path succession proof must verify against it; absence of root falls back to template-procedural succession with explicit governance ratification. |
5. Threat model
| Threat | Mechanism | Mitigation |
|---|---|---|
| Sybil admission | Adversary causes M2 to admit a synthetic identity | KYC-hash gate; off-chain KYC vendor; admission requires governance proof; admission events surface in attestation chain |
| Voting-weight inflation | Admit, resign, re-admit cycle to boost weight, or admit with inflated weight | _validateWeights cap per class; M2 supermajority required for admission; M1.6 monotonic memberIds prevent re-use |
| Identity theft / wallet compromise | Adversary acquires holder’s private key | Q9 hybrid custodial/hardware/paper auth; succession path with _verifySuccessorProof allows override; compliance officer can flag KYC and force re-verification |
| KYC bypass | Caller invokes state-changing function with stale KYC | Modifier kycCurrent reverts; kycExpiresAt enforced |
| Distribution-share overflow | Sum > 10_000 bps via reentrancy or out-of-order updates | Atomic _validateDistributionShareInvariant on every mutation; nonReentrant on admission/succession/removal |
| Successor-root tampering | Adversary persuades governance to set a successor root that pre-authorizes their own admission | setSuccessorRoot requires governance proof; M2 supermajority; 30-day delay (per M6); guardian veto (M2) |
| Removal-as-coup | Coalition removes a dissenting member via M2 majority | removeMember requires supermajority threshold (template-defined; see M2); guardian veto applies; cultural-guardian veto in C/F-with-C |
| Front-running of admission | MEV attack on admission to extract value | Admission has no economic side-effect that can be MEV-extracted; distribution share is set at admission and unchanged until governance acts |
| PII leak | An on-chain field reveals KYC content | kycRefHash is bytes32 opaque pointer; no PII on-chain by construction (M1.9) |
6. Per-template variations
| Template | Seat composition | Successor mechanic | Notable variations |
|---|---|---|---|
| A — Solo | Landowner-primary + family seats (1–4) + Landseed steward | Operating-agreement inheritance (Tier 1 only); merkle-tree pre-declaration if Tier 2 ever applied | KYC re-verification on inheritance event; family-disagreement fallback per OA |
| B — Land Trust | Land-trust-officer + Landseed steward (+ optional independent stewardship advisor) | Institutional procedure (board action); successor land trust on M&A inherits per OA | OA carves out measurement-standing if conservation easement involves same trust |
| C — Indigenous | Community-council seats (multiple) + cultural-guardian + elder seats + Landseed steward | Community-determined; DAO ratifies procedurally, does not vote substantively | Successor-determination is community-internal; on-chain ratification is procedural; cultural-guardian seat has distinct veto class (DECISION_CULTURAL) |
| D — Corporate | Corporate officer + ESG-observer + Landseed steward | Corporate succession (board action, M&A) | Quarterly disclosure obligations encoded in OA; no FPIC class |
| E — Sovereign | Sovereign-agency designee + local management + Landseed steward | Per agency succession; political-disruption fallback | Sovereign seat has residual authority on national-policy class; treaty-reporting OA hook |
| F — Hybrid | Composed seats from underlying templates (e.g., E + C) | Per-seat: each follows underlying template’s mechanic | Cross-stakeholder dispute path required (M2); per-deployment audit required |
| G — Stewardship | Foundation officers + Landseed steward | Foundation succession | All distribution to stewardship reserve (M3 reduces in size) |
7. External dependencies
- M2 (Governance) — caller of
admitMember,removeMember,setSuccessorRoot - M3 (Economics) — reads
getDistributionShareBps - M5 (Attestation hooks) — reads beneficiary set for distribution-event correlation only
- M6 (Upgrade Path) — controls upgradeability of M1 itself
- Off-chain KYC vendor — produces
kycRefHash; vendor outage stalls admissions but does not affect existing operations (vendor selection counsel-confirmation required) - Compliance officer multi-sig — external multi-sig contract address held in
complianceOfficerSet; no DAO-internal cycle
8. Audit-scoping notes
- Estimated audit hours: 60–90 hours for M1 standalone in initial library audit
- Required auditor expertise: Solidity; access-control and modifier patterns; merkle-proof verification (for
successorRoot); familiarity with OpenZeppelin AccessControl or equivalent - Highest-risk areas: succession path (especially Template C’s procedural-ratification distinction); distribution-share invariant under reentrancy; KYC expiry handling under clock manipulation; opacity of
kycRefHash - Test coverage required: 100% for
admitMember,succeedMember,removeMember,_validateDistributionShareInvariant; fuzzing on weight/share inputs; symbolic execution on M1.1 non-transferability invariant - Known pseudocode gaps:
_verifySuccessorProofis template-dependent; per-template instantiation must be re-audited - Audit-first dependency: M1 must be audited before M2, M3, or M5 since they read M1 state
M2 — Governance
1. Purpose
The decision-making layer. Encodes proposal types, voting rules, supermajority thresholds, time delays, FPIC checkpoints, and guardian vetoes. M2 is the only module authorized to mutate M1, M3 parameters, M4 plan-version pointer, and M5 configuration. M6 upgrades go through M2 with additional time-delay and guardian gates.
2. Tier 1 implementation
Encoded in operating agreement:
- Decision classes (use, methodology, treasury, cultural where applicable, stewardship-reserve, module-upgrade) and which member classes vote on each
- Voting thresholds per decision class (simple majority, supermajority, unanimous)
- Notice periods per decision class (typically 60 days for methodology adoption; 30 days for module upgrade)
- FPIC checkpoint procedures (Template C/F-with-C only)
- Methodology-guardian veto procedure (Landseed-officer multi-sig); cultural-guardian veto (community-designated)
- Quorum requirements; tie-breaking; abstention treatment
- Recordkeeping: written notice, signed ratification, retention period
- Dispute and mediation clauses
Encoded in multi-sig:
- Treasury-action ratification by multi-sig signature (typically 2-of-3 for A/B; N-of-M composed for F)
- Daily/per-transaction limits below which multi-sig acts without separate operating-agreement procedure
- Emergency procedures for guardian veto execution
Held off-chain by Landseed compliance:
- Proposal records (PDF, signed scans, board-meeting minutes for B)
- FPIC deliberation transcripts (Template C only; community-controlled retention)
3. Tier 2 implementation
3.1 State variables
struct Proposal {
uint256 id;
address proposer; // must hold a member seat with proposer rights
uint8 decisionClass; // see DECISION_* constants from M1
uint8 proposalKind; // ADMIT_MEMBER, REMOVE_MEMBER, AMEND_PARAMETER,
// RATIFY_PLAN, UPGRADE_MODULE, EXECUTE_TREASURY,
// ADOPT_METHODOLOGY, RATIFY_SUCCESSOR, ...
bytes32 contentHash; // hash of off-chain proposal description (IPFS / Software
// Heritage)
bytes payload; // calldata to execute on success
address target; // contract to call on execution
uint64 createdAt;
uint64 votingStartsAt; // createdAt + noticePeriod[decisionClass]
uint64 votingEndsAt;
uint64 timelockEndsAt; // votingEndsAt + delay[decisionClass]
uint64 guardianVetoEndsAt; // timelockEndsAt + guardianWindow
uint8 status; // PENDING, ACTIVE, PASSED, FAILED, VETOED, EXECUTED, EXPIRED
uint256 yesWeight; // weighted by voting power for decisionClass
uint256 noWeight;
uint256 abstainWeight;
bool fpicRequired; // C / F-with-C only
bytes32 fpicEvidenceHash; // 0 until cultural guardian non-objection / community
// ratification recorded
}
mapping(uint256 => Proposal) internal _proposals;
mapping(uint256 => mapping(uint256 => bool)) internal _hasVoted;
// proposalId => memberId => voted
uint256 public nextProposalId;
mapping(uint8 => uint64) public noticePeriod; // by decisionClass; seconds
mapping(uint8 => uint64) public votingPeriod; // by decisionClass; seconds
mapping(uint8 => uint64) public timelockDelay; // by decisionClass; seconds
mapping(uint8 => uint256) public passingThresholdBps;
// basis points of total weight needed
mapping(uint8 => uint256) public quorumBps; // basis points of total weight
uint64 public guardianWindow; // typically 7–30 days
address public methodologyGuardian; // multi-sig (Landseed officers)
address public culturalGuardian; // 0x0 unless C / F-with-C
mapping(uint8 => bool) public requiresMethodologyGuardianNonObjection;
mapping(uint8 => bool) public requiresCulturalGuardianNonObjection;
mapping(uint8 => bool) public requiresFPIC; // by decisionClass
3.2 Public function signatures
function propose(
uint8 decisionClass,
uint8 proposalKind,
bytes32 contentHash,
address target,
bytes calldata payload
) external onlyMember returns (uint256 proposalId);
function castVote(uint256 proposalId, uint8 support)
external onlyMember kycCurrent(memberIdOfSender);
// support: 0 = no, 1 = yes, 2 = abstain
function recordFpicEvidence(uint256 proposalId, bytes32 evidenceHash)
external onlyCulturalGuardian;
// For C / F-with-C: cultural guardian or community council records FPIC ratification
function vetoMethodology(uint256 proposalId)
external onlyMethodologyGuardian;
// callable only during guardianVetoEndsAt window;
// restricted to proposals affecting ecological-condition floor
function vetoCultural(uint256 proposalId)
external onlyCulturalGuardian;
// restricted to DECISION_CULTURAL or culturally-relevant proposals
function execute(uint256 proposalId) external;
// anyone may trigger; reverts unless status == PASSED && now > timelockEndsAt
// && guardianVetoEndsAt has passed && all required non-objections recorded
function cancel(uint256 proposalId) external onlyProposer;
// proposer can withdraw before votingStartsAt
// Read-side
function getProposal(uint256 proposalId) external view returns (Proposal memory);
function votingPower(uint256 memberId, uint8 decisionClass)
external view returns (uint256);
function quorumReached(uint256 proposalId) external view returns (bool);
function passingThresholdMet(uint256 proposalId) external view returns (bool);
3.3 Internal/private functions
function _validateProposal(uint8 decisionClass, uint8 proposalKind) internal view;
function _snapshotVotingWeights(uint256 proposalId) internal;
// captures M1 weights at proposal creation; prevents re-admission gaming
function _checkFpicCompliance(uint256 proposalId) internal view returns (bool);
function _checkGuardianClearance(uint256 proposalId) internal view returns (bool);
function _enforceTimelock(uint256 proposalId) internal view;
function _executeCall(address target, bytes memory payload) internal returns (bytes memory);
3.4 Events
event ProposalCreated(
uint256 indexed proposalId,
address indexed proposer,
uint8 indexed decisionClass,
uint8 proposalKind,
bytes32 contentHash,
uint64 votingStartsAt,
uint64 votingEndsAt
);
event VoteCast(
uint256 indexed proposalId,
uint256 indexed memberId,
uint8 support,
uint256 weight
);
event FpicEvidenceRecorded(uint256 indexed proposalId, bytes32 evidenceHash);
event ProposalPassed(uint256 indexed proposalId, uint256 yesWeight, uint256 totalWeight);
event ProposalFailed(uint256 indexed proposalId, uint8 reason);
event ProposalVetoed(uint256 indexed proposalId, address indexed guardian, uint8 vetoKind);
event ProposalExecuted(uint256 indexed proposalId, bytes returnData);
event ProposalCancelled(uint256 indexed proposalId);
event ParameterChanged(uint8 indexed decisionClass, bytes32 indexed paramKey, uint256 oldVal, uint256 newVal);
3.5 Modifiers and access control
modifier onlyMember(); // msg.sender is active holder of an M1 memberId
modifier onlyMethodologyGuardian(); // msg.sender == methodologyGuardian multi-sig
modifier onlyCulturalGuardian(); // msg.sender == culturalGuardian; reverts if 0x0
modifier onlyProposer(uint256 proposalId);
modifier inVotingWindow(uint256 proposalId);
modifier afterTimelock(uint256 proposalId);
4. Invariants
| # | Invariant |
|---|---|
| M2.1 | One vote per member per proposal: _hasVoted[proposalId][memberId] enforces idempotency. |
| M2.2 | Voting-weight snapshot immutability: weights are snapshotted at proposalId creation; subsequent M1 mutations do not change this proposal’s tallies. |
| M2.3 | No execution before timelock: execute reverts unless block.timestamp > timelockEndsAt && block.timestamp > guardianVetoEndsAt. |
| M2.4 | Guardian veto is irreversible per proposal: once vetoed, status = VETOED and proposal cannot be re-executed without a new proposal. |
| M2.5 | FPIC gate in C/F-with-C: for proposals where fpicRequired == true, execute reverts unless fpicEvidenceHash != 0. |
| M2.6 | Quorum required: passingThresholdMet requires quorumReached. |
| M2.7 | Self-call only via execution: M2 cannot mutate its own parameters except via execute of a UPGRADE_MODULE or AMEND_PARAMETER proposal. |
| M2.8 | Methodology guardian cannot veto cultural decisions and vice versa: vetoCultural reverts on non-cultural decision class; vetoMethodology reverts on cultural-only decisions. The two guardian roles are non-overlapping (per current architecture). |
| M2.9 | Proposer must be active member at proposal creation: propose checks M1.isActive. |
| M2.10 | Timelock cannot be set to zero by parameter change: minimum non-zero timelock for DECISION_UPGRADE is 30 days hardcoded as a MIN_UPGRADE_DELAY constant. |
| M2.11 | Re-entrant execution prevented: execute is nonReentrant; payloads cannot recursively call execute on the same proposal. |
5. Threat model
| Threat | Mechanism | Mitigation |
|---|---|---|
| Majority capture | Coalition with >threshold weight passes self-serving proposals | Supermajority for high-stakes classes (UPGRADE, METHODOLOGY); guardian veto on destruction; FPIC in C; cultural guardian veto in C |
| Vote-weight gaming | Re-admit a member with inflated weight before a critical vote | M2.2 snapshot immutability; M1 admission requires governance; admission notice period exposes the maneuver |
| Front-running governance proposals | MEV-bot front-runs propose to claim rewards or block | No economic reward for proposing; proposer is identified; proposals are not value-extractable |
| Flash-loan voting (theoretical) | Acquire voting weight via flash loan | Non-transferable membership (M1.1) eliminates this attack vector by construction |
| Censorship of votes | Block producer censors castVote near votingEndsAt | Voting periods are days/weeks long; sub-block-level censorship is not material |
| Guardian collusion | Methodology guardian and cultural guardian collude to veto everything | M2.8 limits each guardian’s veto domain; guardian roles are external multi-sigs with documented signers; collusion requires both multi-sigs to act in concert; recourse is M2 governance to replace guardians (with Foundation-level escalation) |
| Guardian unresponsiveness | Methodology guardian is offline during a critical proposal | guardianVetoEndsAt is a fixed window; absence of veto = clearance; quorum on the multi-sig can be re-established off-chain |
| Timelock bypass via reentrancy | Payload calls execute recursively | M2.11 nonReentrant; M2.3 timestamp gate |
| FPIC forgery | Bad actor records false fpicEvidenceHash | recordFpicEvidence is onlyCulturalGuardian; cultural guardian is community-designated and held to community-internal audit (NURJ §IV.E) |
| Time-warp attacks (block.timestamp manipulation) | Validator manipulates timestamp to bypass timelock | Timelocks measured in days; ±15s validator drift immaterial; flagged for L2-specific audit |
| Proposal-storm DoS | Member spams proposals to clog the pipeline | Member-class proposer rights gate; off-chain proposal-content cost (IPFS pinning) imposes friction; M2 may add per-member proposal cooldowns at deployment |
Calldata smuggling in payload | Proposal payload calls unauthorized functions on target | _executeCall only invokes whitelisted targets; M1, M3, M4, M5, M6 contracts each enforce onlyGovernance independently |
6. Per-template variations
| Template | Voting model | FPIC | Cultural guardian | Notable |
|---|---|---|---|---|
| A — Solo | Landowner supermajority on use; Landseed-ratified methodology; multi-sig treasury | n/a | n/a | Tier 1; not on-chain. If Tier 2 ever applied, simple Governor pattern. |
| B — Land Trust | Institutional board-style majority; two-key on material plan changes | n/a | n/a | Tier 1; institutional governance referenced in OA. |
| C — Indigenous | Community-council supermajority; FPIC-gated on every methodology/plan change; cultural-guardian non-objection on culturally relevant actions | Required for methodology adoption, plan amendment, sensor deployment, external publication, non-community-member admission | Required seat; veto on DECISION_CULTURAL | Tier 2 mandatory; per-deployment audit; longer notice periods (60 days minimum) |
| D — Corporate | Corporate-officer-board majority; ESG observer non-binding | n/a | n/a | Quarterly disclosure obligation; ESG observer has read access to proposal stream |
| E — Sovereign | Sovereign-residual on national-policy class; operational delegation | n/a | n/a | Treaty-reporting obligation; political-disruption fallback (operating-agreement-defined) |
| F — Hybrid | Decision-class routing across composed seats; mediation-first dispute resolution | Required if C-component | Required if C-component | Per-deployment audit; explicit conflict resolution mechanism in M2 (mediator address; fallback to OA arbitration) |
| G — Stewardship | Foundation board-style majority | n/a | n/a | Tier 1; foundation procedures govern |
7. External dependencies
- M1 (Beneficiary Registry) — voting weights and active-member set
- M3, M4, M5, M6 — execution targets
- Methodology guardian multi-sig — external multi-sig contract (Landseed officers); address stored in
methodologyGuardian - Cultural guardian — for C/F-with-C; community-designated multi-sig
- Off-chain proposal content — IPFS / Software Heritage pinning (not yet built); content-hash referenced on-chain
8. Audit-scoping notes
- Estimated audit hours: 120–180 hours for M2 standalone in initial library audit (largest module by complexity)
- Required auditor expertise: Solidity governance patterns; OpenZeppelin Governor / Aragon OSx / Tally Governor familiarity strongly preferred; timelock and access-control patterns; reentrancy and snapshot semantics
- Highest-risk areas: weight snapshot consistency under M1 mutations during a proposal; timelock bypass via payload calldata; guardian-veto window edge cases; FPIC-gate enforcement for C; cross-module call routing
- Test coverage required: 100% for
propose,castVote,execute,vetoMethodology,vetoCultural; fuzzing on threshold and quorum boundaries; invariant testing on M2.2, M2.3, M2.5, M2.10 - Audit-first dependency: depends on M1 audit completion. Cross-module integration tests with M3/M4/M5/M6 happen after each is independently audited.
M3 — Economics
1. Purpose
Distribution rules. Splits inflows from credit sales according to the template’s distribution philosophy. The benefits model lives here. M3 is parametric within bright-line constraints (no tradeable units, no DeFi, conservative treasury). It reads M1 for distribution shares and M5 for inflow events (so that distributions can be correlated with the attestation that produced them).
2. Tier 1 implementation
Encoded in operating agreement:
- Distribution percentages per recipient (within parameter ranges; see deploy parameters below)
- Distribution cadence (annual, quarterly, on-event)
- Distribution form: cash only (Bright Line 6 in
04-perimeter/); in-kind credit distribution only if Q1 in06-risks/resolves favorably (counsel-confirmation required; currently set aside) - Stewardship reserve drawdown procedure (documented work; multi-sig signoff)
- Landseed protocol fee rate (2–5%)
- Reserve maintenance and reinvestment policy
- Tax-treatment representations (counsel-confirmation required per jurisdiction)
Encoded in multi-sig:
- Disbursement transactions (USDC or stablecoin-of-record)
- Daily/per-transaction limits
- Multi-sig threshold for distribution execution
Held off-chain by Landseed compliance:
- Per-distribution worksheets (pre-distribution computation, recipient acknowledgments)
- Tax-form generation (1099-MISC for US; equivalents per jurisdiction)
- Annual financial statements (Form 990 for nonprofit-held templates)
3. Tier 2 implementation
3.1 State variables
struct Recipient {
uint8 kind; // RECIPIENT_MEMBER, RECIPIENT_RESERVE, RECIPIENT_PROTOCOL_FEE,
// RECIPIENT_COMMUNITY_FUND, RECIPIENT_SOVEREIGN_FUND,
// RECIPIENT_STEWARDSHIP_RESERVE, RECIPIENT_BUFFER_POOL_FEE
address payoutAddress; // wallet or contract receiving funds (e.g., Foundation
// buffer-pool address per `05-interfaces/05`)
uint256 shareBps; // basis points of net inflow
uint256 memberId; // M1 reference if kind == RECIPIENT_MEMBER, else 0
bool active;
}
mapping(uint256 => Recipient) internal _recipients; // recipientId => Recipient
uint256 public nextRecipientId;
uint256 public totalShareBps; // must always equal 10_000
uint8 public distributionCadence; // ANNUAL, QUARTERLY, ON_EVENT
uint64 public lastDistributionAt;
uint64 public nextDistributionWindowOpensAt;
address public stablecoinOfRecord; // USDC or template-deployment-defined
// *(not yet locked; specific address
// configured at deployment)*
uint256 public stewardshipReserveBalance; // accounting balance, not held tokens
uint256 public protocolFeeBps; // 200..500 (2–5%)
uint256 public bufferPoolFeeBps; // 0 here; buffer is taken pre-DAO at
// registry layer (per `05-interfaces/05`)
mapping(bytes32 => uint256) internal _inflowsByAttestation;
// attestationReceiptHash => amount
mapping(bytes32 => bool) internal _attestationDistributed;
3.2 Public function signatures
function recordInflow(
uint256 amount,
bytes32 attestationReceiptHash, // links revenue to specific cryptographically
// attested credit-issuance event (M5)
bytes calldata registryProof // proves inflow originated from registry
// (per `05-interfaces/04`)
) external onlyAttestationHook;
function distribute(bytes32 attestationReceiptHash) external nonReentrant;
// anyone may trigger; reverts unless cadence window open or ON_EVENT triggered
// executes split per active recipients
function addRecipient(
uint8 kind,
address payoutAddress,
uint256 shareBps,
uint256 memberId
) external onlyGovernance returns (uint256 recipientId);
function updateRecipientShare(uint256 recipientId, uint256 newShareBps)
external onlyGovernance;
function deactivateRecipient(uint256 recipientId) external onlyGovernance;
function drawStewardshipReserve(
uint256 amount,
address recipient,
bytes32 documentationHash, // hash of work-order / invoice / approval record
bytes calldata governanceProof
) external onlyGovernance;
function setCadence(uint8 newCadence, uint64 newWindowStart) external onlyGovernance;
function setStablecoinOfRecord(address newStablecoin)
external onlyGovernance;
// requires M2 supermajority + 30-day delay; ties to migration-safety commitment
// Read-side
function getRecipient(uint256 recipientId) external view returns (Recipient memory);
function previewDistribution(uint256 amount)
external view returns (uint256[] memory shares, address[] memory payouts);
function distributionHistory(bytes32 attestationReceiptHash)
external view returns (uint256 amount, uint64 distributedAt);
3.3 Internal/private functions
function _validateShareSum() internal view;
// sum of active shareBps == 10_000 exactly
function _allocate(uint256 amount) internal returns (uint256[] memory);
function _settle(uint256[] memory shares, Recipient[] memory rs) internal;
// ERC-20 transfer per recipient; failure reverts the entire batch (atomicity)
function _accrueStewardshipReserve(uint256 amount) internal;
3.4 Events
event InflowRecorded(
bytes32 indexed attestationReceiptHash,
uint256 amount,
address stablecoin
);
event DistributionExecuted(
bytes32 indexed attestationReceiptHash,
uint256 totalAmount,
uint64 timestamp
);
event RecipientPaid(
uint256 indexed recipientId,
address indexed payout,
uint256 amount,
uint8 kind
);
event RecipientAdded(uint256 indexed recipientId, uint8 kind, uint256 shareBps);
event RecipientShareUpdated(uint256 indexed recipientId, uint256 oldBps, uint256 newBps);
event RecipientDeactivated(uint256 indexed recipientId);
event StewardshipDrawn(uint256 amount, address recipient, bytes32 documentationHash);
event CadenceChanged(uint8 oldCadence, uint8 newCadence, uint64 windowStart);
event StablecoinChanged(address oldStablecoin, address newStablecoin);
3.5 Modifiers and access control
modifier onlyAttestationHook(); // msg.sender == address(M5)
modifier onlyGovernance(); // msg.sender == address(M2)
modifier nonReentrant();
modifier inDistributionWindow();
4. Invariants
| # | Invariant |
|---|---|
| M3.1 | Sum-of-shares == 100%: Σ shareBps for active recipients == 10_000. Enforced atomically on add/update/deactivate. |
| M3.2 | Cash-only: distributions are in stablecoinOfRecord. No path emits ERC-20 transfers other than the configured stablecoin. (Bright Line 6.) |
| M3.3 | No DeFi yield: M3 has no lend, stake, swap, or external-protocol-call surface beyond ERC-20 transfer to recipients. |
| M3.4 | Inflow→distribution traceability: every distribution references a non-zero attestationReceiptHash; _attestationDistributed[hash] prevents double-distribution. |
| M3.5 | Stewardship reserve only by governance: drawStewardshipReserve requires onlyGovernance. |
| M3.6 | Atomic settle: a failure on any recipient transfer reverts the entire distribute call; partial distributions are impossible. |
| M3.7 | No tradeable units emitted: M3 emits no ERC-20-token-mint or ERC-721-mint operation. (Per Principle 2.) |
| M3.8 | No cross-property inflows: M3 accepts inflows only from the registered registry contract for this property; cross-property routing is prevented at recordInflow via registryProof validation. |
| M3.9 | Protocol fee rate bounded: protocolFeeBps ∈ [200, 500] (2–5%); changes require M2 supermajority. |
5. Threat model
| Threat | Mechanism | Mitigation |
|---|---|---|
| Distribution-share manipulation | Adversary updates shares pre-distribution to redirect funds | updateRecipientShare is onlyGovernance (M2 vote + timelock + guardian window); 30-day delay exposes the maneuver |
| Double-distribution | Same attestationReceiptHash distributed twice | M3.4 mapping prevents replay |
| Inflow spoofing | Adversary calls recordInflow with fake registryProof | onlyAttestationHook modifier; registryProof validated against registry contract address (per 05-interfaces/04) |
| Cross-property contamination | Inflows from one property reach another’s M3 | M3.8; per-property isolation hardcoded at deployment |
| Stablecoin migration risk (USDC depeg / sanction freeze) | Configured stablecoin loses value or freezes | setStablecoinOfRecord migration path; 30-day delay; graceful migration plan documented in OA |
| Fee-rate creep | Protocol fee gradually raised by Landseed-aligned majority | M3.9 hardcoded bounds; M2 supermajority; counsel-confirmed at deployment |
| DeFi-integration creep | Future M3 upgrade adds yield farming | M6 upgrade path requires guardian veto; “no DeFi” is a Bright Line; deviating triggers Foundation escalation |
Reentrancy on distribute | Recipient contract reenters during transfer | nonReentrant; recipients should be EOAs or audited multi-sigs (validated at addRecipient) |
| Stewardship-reserve raid | Drawn without documented work | documentationHash required; onlyGovernance; multi-sig signoff on Tier 1 mirror |
| Buffer-pool double-take | Buffer fee deducted at registry AND at DAO | M3.bufferPoolFeeBps == 0 by construction; buffer is taken pre-DAO per 05-interfaces/05; auditor must verify zero |
| Recipient-address poisoning | Governance adds an attacker-controlled recipient | addRecipient requires M2 + timelock + guardian window; addresses logged in events for outside scrutiny |
6. Per-template variations
The distribution waterfall differs substantially across templates. Parameter ranges:
| Recipient | Template A | Template B | Template C | Template D | Template E | Template F | Template G |
|---|---|---|---|---|---|---|---|
| Direct landowner / institutional principal | 60–85% | 60–80% (land trust) | n/a (community fund) | 70–90% (corporate) | n/a | composed | n/a (foundation) |
| Community fund | n/a | n/a | 40–80% | n/a | n/a | composed | n/a |
| Sovereign environmental fund | n/a | n/a | n/a | n/a | 60–80% | composed | n/a |
| Stewardship reserve (property-specific) | 5–25% | 10–25% | 5–15% | 5–15% | 10–25% | composed | All to stewardship |
| Cultural-guardian discretionary fund | n/a | n/a | 5–15% | n/a | n/a | composed (if C component) | n/a |
| Landseed protocol fee | 2–5% | 2–5% | 2–5% | 2–5% | 2–5% | 2–5% | 2–5% |
| Reserve / residual | 5–25% | 5–10% | per community | 5–25% | per agency | composed | per foundation |
Sum must equal 100% per deployment. Specific values are configured in operating-agreement (Tier 1) or at constructor / governance-set parameters (Tier 2).
Template C: distribution-within-community-fund logic is intentionally not encoded in M3. The community fund payout is a single payoutAddress from M3’s perspective; how the community then distributes internally (elder fund, language preservation, youth education) is community-internal and off-DAO. This is a NURJ-paper-grounded choice (data sovereignty; cultural protocols are not Landseed’s to encode).
7. External dependencies
- M1 (Registry) — distribution shares for
RECIPIENT_MEMBERrecipients - M5 (Attestation Hooks) — calls
recordInflow - Registry contract (off-DAO) — origin of inflows; attestation receipts; per
05-interfaces/04 - Methodology Foundation buffer pool — separate registry account; receives buffer-pool credits at issuance time, not through M3 (per
05-interfaces/05) - Stablecoin contract — USDC or deployment-configured; (specific address counsel-and-treasury-confirmation required)
8. Audit-scoping notes
- Estimated audit hours: 60–80 hours for M3 standalone in initial library audit
- Required auditor expertise: Solidity; ERC-20 transfer patterns; reentrancy and atomicity guarantees; familiarity with payment-router and PaymentSplitter patterns (OpenZeppelin)
- Highest-risk areas: invariant M3.1 (sum-of-shares) under concurrent governance updates; reentrancy on settle; cross-property-contamination-prevention proof; stewardship-reserve drawdown audit trail
- Test coverage required: 100% for
recordInflow,distribute,addRecipient,updateRecipientShare; fuzzing on share sums; reentrancy fuzzing; multi-recipient settlement testing - Per-template variation: distribution shares are parameter-only, not code; M3 contract code is uniform across templates A through G
- Audit-first dependency: depends on M1; integrates with M5
M4 — Management Plan Ratification
1. Purpose
Versioned forest, invasive-species, harvest, and restoration plans. The LLC/DAO ratifies each version through M2; the active version pointer is M4 state. M4 liberates v1.2’s static prescription content from the deed by making it amendable through governance. M4 stores only content hashes; full plan documents live off-chain (IPFS / Software Heritage / counsel-archived).
2. Tier 1 implementation
Encoded in operating agreement:
- Plan-amendment procedure (proposal, notice, ratification by member signature)
- Effective-period definition (when does ratified version take effect)
- Plan-content scope: forestry, invasive species, riparian buffers, harvest schedules, restoration milestones
- Reference to current plan version by content hash
- Coordination with conservation easement (Template B) — “more restrictive controls” rule per element 10 of NRD-lite
Held off-chain by Landseed compliance:
- Plan-content PDFs (signed)
- Ratification records (board minutes for B; landowner signatures for A)
- Plan-version history archive
3. Tier 2 implementation
3.1 State variables
struct PlanVersion {
bytes32 contentHash; // hash of plan document (off-chain; IPFS or
// Software Heritage anchor)
bytes32 storageUri; // pointer to retrieval location (URI hash)
uint64 ratifiedAt;
uint64 effectiveFrom;
uint64 effectiveUntil; // 0 = open-ended until next ratification
uint256 ratifyingProposalId; // M2 proposalId that ratified
uint8 status; // PROPOSED, RATIFIED, ACTIVE, SUPERSEDED, EXPIRED, REVOKED
bytes32 priorVersionHash; // chain-of-versions integrity
}
mapping(uint256 => PlanVersion) internal _versions; // versionId => PlanVersion
uint256 public nextVersionId;
uint256 public activeVersionId; // 0 if none
mapping(bytes32 => uint256) internal _versionByHash;
3.2 Public function signatures
function proposePlanVersion(
bytes32 contentHash,
bytes32 storageUri,
uint64 effectiveFrom,
bytes32 priorVersionHash
) external onlyMember returns (uint256 versionId);
function ratifyPlanVersion(uint256 versionId, uint256 ratifyingProposalId)
external onlyGovernance;
function activatePlanVersion(uint256 versionId) external onlyGovernance;
// promotes RATIFIED → ACTIVE; supersedes prior active version
function revokePlanVersion(uint256 versionId, bytes calldata governanceProof)
external onlyGovernance;
// rare; emergency only
// Read-side
function getActiveVersion() external view returns (PlanVersion memory);
function getVersion(uint256 versionId) external view returns (PlanVersion memory);
function versionByHash(bytes32 contentHash) external view returns (uint256 versionId);
3.3 Internal/private functions
function _verifyChainIntegrity(uint256 newVersionId) internal view;
// priorVersionHash must equal contentHash of currently active version (or 0 for genesis)
function _supersedeActive(uint256 newVersionId) internal;
3.4 Events
event PlanVersionProposed(
uint256 indexed versionId,
bytes32 indexed contentHash,
bytes32 priorVersionHash,
address proposer
);
event PlanVersionRatified(
uint256 indexed versionId,
uint256 indexed ratifyingProposalId,
uint64 effectiveFrom
);
event PlanVersionActivated(
uint256 indexed versionId,
uint256 indexed supersededVersionId
);
event PlanVersionRevoked(uint256 indexed versionId, bytes32 reasonHash);
3.5 Modifiers and access control
modifier onlyMember();
modifier onlyGovernance();
modifier validVersion(uint256 versionId);
4. Invariants
| # | Invariant |
|---|---|
| M4.1 | At most one active version: at any block, exactly one or zero versionId has status == ACTIVE. |
| M4.2 | Chain-of-versions integrity: priorVersionHash of a new version equals contentHash of the prior active version (or zero for genesis). |
| M4.3 | Content-addressed: versionByHash[contentHash] is uniquely defined; no two versions share a contentHash. |
| M4.4 | Ratification through M2 only: ratifyPlanVersion and activatePlanVersion are onlyGovernance. |
| M4.5 | No content on-chain: M4 never stores plan-document text; only the hash and URI. |
| M4.6 | Effective-from monotonicity: a new active version’s effectiveFrom is ≥ the prior version’s ratifiedAt. |
5. Threat model
| Threat | Mechanism | Mitigation |
|---|---|---|
| Plan-content swap | Off-chain document at URI changes after ratification | Content-addressed (M4.3); URI is a hint, hash is the truth; auditors and members verify hash before voting |
| Storage availability | IPFS pin lapses; document unrecoverable | Triple archive: IPFS + Software Heritage + counsel-archived paper (per Q-archival-1 in 06-risks/) |
| Ratification of malicious plan | Coalition ratifies plan that violates ecological-condition floor | M2 supermajority; methodology guardian veto (Landseed); cultural guardian non-objection in C |
| Chain-of-versions split | Two competing versions ratified concurrently | M2 sequential proposal execution; chain-integrity check on activation |
| Genesis-version dispute | First version’s priorVersionHash == 0 is contested | Genesis recorded at deployment; cross-referenced in OA and NRD-lite |
| Revocation abuse | Active plan revoked without replacement, leaving property in plan-less state | revokePlanVersion requires governance; OA fallback to prior version on revocation; flagged for audit edge-case testing |
6. Per-template variations
| Template | Plan amendment procedure | Notable |
|---|---|---|
| A — Solo | Standard OA amendment (landowner signature) | Tier 1 |
| B — Land Trust | Institutional approval (board action) + Landseed methodology check | Tier 1; coordination with conservation easement |
| C — Indigenous | FPIC-gated: community-council majority + cultural guardian non-objection | 60-day deliberation period mandatory |
| D — Corporate | Institutional approval + ESG observer review | Quarterly disclosure of plan changes |
| E — Sovereign | Sovereign approval (agency action) | Treaty-reporting obligation |
| F — Hybrid | Composed: each component approval required | Cross-component conflict resolution per M2 |
| G — Stewardship | Foundation approval | Tier 1 |
7. External dependencies
- M2 (Governance) — proposal/ratification trigger
- Off-chain content store — IPFS, Software Heritage, paper (triple-archive not yet built; commitment per Q-archival-1)
- NRD-lite hash pinning — methodology hash referenced; plan version is separate from methodology version
8. Audit-scoping notes
- Estimated audit hours: 30–50 hours for M4 standalone
- Required auditor expertise: Solidity; content-addressed-storage patterns; state-machine verification
- Highest-risk areas: chain-of-versions integrity (M4.2); concurrent ratification race conditions; revocation edge cases
- Test coverage required: 100% for
proposePlanVersion,ratifyPlanVersion,activatePlanVersion; chain-integrity property tests; concurrent-proposal fuzzing
M5 — Attestation Hooks
1. Purpose
The Layer 2 vehicle consumes EC-M-1.1 attestations from registry attestation receipts. M5 listens for cryptographically attested credit-issuance and revenue events from the registry function (per 05-interfaces/04-registry-function-specification.md), records them in DAO state, and surfaces inflows to M3. M5 is a passive listener: the DAO does not issue credits; it consumes evidence of issuance and sale.
2. Tier 1 implementation
Manual recording in books and records:
- Each attestation receipt (ECI score, threat multiplier, methodology version, issuance timestamp) recorded in LLC books
- Periodic audits verify books match attestation chain (per registry annual ops audit)
- Distribution events reference the specific attestation that produced them
- No on-chain hooks at Tier 1; the cryptographic chain runs at Layer 3 and is consumed manually
Held off-chain by Landseed compliance:
- Per-property attestation receipt archive
- Quarterly attestation summaries
- Methodology-version timeline (for chain-of-attestation integrity over multi-year periods)
3. Tier 2 implementation
3.1 State variables
struct AttestationRecord {
bytes32 receiptHash; // canonical hash of the attestation receipt
// (signed by methodology stewards' attestor key)
bytes32 methodologyVersion; // hash-pinned methodology document version
uint256 eciConservativeBp; // conservative ECI bound × 10_000 (fixed-point)
uint256 threatMultiplierBp; // threat multiplier × 10_000
uint256 verifiedAcres; // from NRD-lite + survey
uint256 issuedCredits; // calculated and reported by registry
uint64 receivedAt;
uint8 status; // RECORDED, INFLOW_BOUND, INFLOW_DISTRIBUTED, INVALIDATED
}
mapping(bytes32 => AttestationRecord) internal _attestations; // receiptHash => record
mapping(bytes32 => bytes32) internal _attestationsByMethodologyVersion;
// tracks methodology drift
bytes32 public registryAttestorPubkeyHash; // expected signer's key hash
address public registryContract; // expected origin of `notifyIssuance`
bytes32 public propertyId; // unique per-property identifier
// (NRD-lite hash anchor)
uint256 public totalAttestationsReceived;
uint256 public totalCreditsIssuedRecord;
3.2 Public function signatures
function notifyIssuance(
bytes32 receiptHash,
bytes32 methodologyVersion,
uint256 eciConservativeBp,
uint256 threatMultiplierBp,
uint256 verifiedAcres,
uint256 issuedCredits,
bytes calldata attestorSignature
) external onlyRegistry returns (bool);
function notifyRevenue(
bytes32 receiptHash,
uint256 amount,
bytes calldata registryProof
) external onlyRegistry;
// calls M3.recordInflow(amount, receiptHash, registryProof)
function notifyInvalidation(
bytes32 receiptHash,
bytes32 reasonHash,
bytes calldata foundationProof
) external onlyRegistry;
// marks attestation INVALIDATED; informational; buffer-pool replacement
// happens at registry layer, not in this DAO
// Read-side
function getAttestation(bytes32 receiptHash)
external view returns (AttestationRecord memory);
function attestationsForMethodologyVersion(bytes32 version)
external view returns (bytes32[] memory receiptHashes);
3.3 Internal/private functions
function _verifyAttestorSignature(
bytes32 receiptHash,
bytes calldata signature
) internal view returns (bool);
function _verifyMethodologyPin(bytes32 methodologyVersion) internal view returns (bool);
// confirms methodologyVersion is in the set ratified for this property
// (cross-references M2 governance state; methodology-adoption proposals
// record ratified versions)
3.4 Events
event AttestationReceived(
bytes32 indexed receiptHash,
bytes32 indexed methodologyVersion,
uint256 issuedCredits,
uint256 verifiedAcres
);
event RevenueNotified(
bytes32 indexed receiptHash,
uint256 amount
);
event AttestationInvalidated(
bytes32 indexed receiptHash,
bytes32 reasonHash
);
event RegistryConfigChanged(
address oldRegistry,
address newRegistry,
bytes32 oldAttestorPubkeyHash,
bytes32 newAttestorPubkeyHash
);
3.5 Modifiers and access control
modifier onlyRegistry(); // msg.sender == registryContract
modifier validAttestor(bytes calldata signature, bytes32 messageHash);
modifier methodologyRatified(bytes32 version);
4. Invariants
| # | Invariant |
|---|---|
| M5.1 | DAO does not issue credits: M5 has no mint or issue function. (Per Principle 2 firewall.) |
| M5.2 | Attestation deduplication: each receiptHash is processed at most once for notifyIssuance. |
| M5.3 | Attestor signature required: notifyIssuance requires valid signature against registryAttestorPubkeyHash; reverts otherwise. |
| M5.4 | Methodology pinning: notifyIssuance reverts unless methodologyVersion is in the ratified-for-this-property set. |
| M5.5 | Property isolation: M5 only accepts attestations bearing this propertyId; cross-property receipts are rejected. |
| M5.6 | Revenue traceability: every notifyRevenue references a previously received receiptHash. |
| M5.7 | Invalidation does not unwind distributions: notifyInvalidation is informational; previously distributed inflows are not clawed back from M3. (Buffer-pool replacement happens at registry layer per 05-interfaces/05.) |
| M5.8 | Registry config change is governance-only: RegistryConfigChanged only via M2 supermajority + 30-day delay. |
5. Threat model
| Threat | Mechanism | Mitigation |
|---|---|---|
| Forged attestation | Adversary submits fake receipt | M5.3 signature check against attestor pubkey; M5.4 methodology pin |
| Replay of old attestation | Old receipt resubmitted | M5.2 deduplication |
| Cross-property attestation injection | Attestation for property X submitted to property Y’s M5 | M5.5; propertyId is constructor-immutable or governance-only-changeable |
| Methodology version drift | Attestation references methodology version not ratified for this property | M5.4 ratified-set check; M2 ratifies methodology versions explicitly |
| Attestor key compromise | Attestor private key leaked | Key-rotation procedure; M5.8 governance-only change; key compromise disclosed publicly per registry transparency commitment |
| Registry-contract upgrade attack | New registry contract impersonates old one | M5.8 governance-only change; address is immutable absent explicit M2 supermajority + delay |
| Inflow double-counting | Same revenue event triggers two recordInflow calls in M3 | M3.4 mapping prevents double-distribution; M5 emits single notifyRevenue per receipt |
| Methodology error invalidation propagation | Foundation invalidates attestations broadly | notifyInvalidation is informational; affects future attestation status only; does not retroactively unwind already-distributed inflows |
6. Per-template variations
| Template | M5 implementation |
|---|---|
| A, B, D, E, G — Tier 1 | No on-chain M5; manual recording in LLC books |
| C — Indigenous | Tier 2 mandatory; permissioned read access on attestation events (CARE: authority to control); aggregated ECI public, raw observations community-only |
| F — Hybrid | Tier 2; per-deployment audit; cross-stakeholder access controls |
For Templates A, B, D, E, G the propertyId, receiptHash, and methodology pinning all exist conceptually but are recorded off-chain. The audit-trail rigor is identical; the storage medium differs.
7. External dependencies
- Registry contract — origin of all
notifyIssuanceandnotifyRevenuecalls; per05-interfaces/04 - Methodology Foundation buffer pool — handles invalidations; M5 only mirrors them, does not act on them; per
05-interfaces/05 - M2 — methodology-version ratification governs M5.4
- M3 — receives
recordInflowcalls - Off-chain attestor key — methodology stewards’ signing key; rotation per registry-governance procedure
8. Audit-scoping notes
- Estimated audit hours: 50–70 hours for M5 standalone
- Required auditor expertise: Solidity; signature verification (ECDSA / EIP-712); cross-contract message verification; familiarity with cross-chain or oracle-attestation patterns
- Highest-risk areas: signature verification under chain-id/replay attacks; methodology-version pinning consistency with M2 state; cross-property isolation enforcement; registry-contract upgrade path
- Test coverage required: 100% for
notifyIssuance,notifyRevenue,_verifyAttestorSignature; cross-chain replay testing if multi-chain; methodology-version drift simulation - Audit-first dependency: depends on registry contract spec stability (per
05-interfaces/04); audit M5 in conjunction with registry external-interface review
M6 — Upgrade Path
1. Purpose
Module-by-module upgrade governance with supermajority + time delay + guardian veto. M6 enables the architecture to evolve over the property’s 99-year horizon without re-deploying the entire vehicle. Constitution-guardian veto is the architectural backstop preventing governance from voting to violate per-property isolation, transferability rules, or other founding commitments.
2. Tier 1 implementation
Encoded in operating agreement:
- Standard amendment procedure (proposal documented; signature-based amendment; possibly notarization)
- Required signatories (member majority/supermajority; Landseed steward; cultural guardian if applicable)
- Notice period (30 days minimum; 60 days for material amendments)
- Effective-on-amendment clause
- Constitution-guardian non-objection (per founding-principles list in
00-foundations/03-binding-principles.md) - Recordkeeping (signed amendment archived; new OA version superseding prior)
Encoded in multi-sig:
- For Tier 1, no on-chain code to upgrade; multi-sig signer-set rotation follows OA amendment procedure
- Compliance-officer multi-sig membership changes follow same procedure
3. Tier 2 implementation
3.1 State variables
struct ModuleSlot {
address current;
address pending;
uint64 pendingActivatesAt;
uint64 guardianVetoEndsAt;
bytes32 newCodeHash; // deployed code hash for verifiability
bytes32 auditAttestationHash; // hash of audit report for the new module
uint8 moduleKind; // M1, M2, M3, M4, M5, M6
}
mapping(uint8 => ModuleSlot) internal _slots; // moduleKind => slot
uint64 public constant MIN_UPGRADE_DELAY = 30 days;
uint64 public constant MIN_GUARDIAN_WINDOW = 7 days;
address public constitutionGuardian; // Landseed-officer multi-sig
mapping(uint8 => address) public moduleGuardian; // optional per-module guardian
3.2 Public function signatures
function proposeUpgrade(
uint8 moduleKind,
address newImplementation,
bytes32 newCodeHash,
bytes32 auditAttestationHash,
bytes calldata governanceProof // M2 supermajority proof
) external onlyGovernance returns (uint256 upgradeId);
function vetoConstitutional(uint8 moduleKind)
external onlyConstitutionGuardian;
// callable in window guardianVetoEndsAt; restricted to violations of
// founding principles list
function vetoModule(uint8 moduleKind)
external onlyModuleGuardian(moduleKind);
// module-specific guardian (e.g., methodology guardian for M5 changes,
// cultural guardian for M2 changes affecting cultural-decision class)
function activateUpgrade(uint8 moduleKind) external;
// anyone may trigger after guardianVetoEndsAt;
// performs storage migration, swap address, emit event
function abortUpgrade(uint8 moduleKind, bytes calldata governanceProof)
external onlyGovernance;
// Read-side
function getModule(uint8 moduleKind) external view returns (ModuleSlot memory);
function isUpgradePending(uint8 moduleKind) external view returns (bool);
3.3 Internal/private functions
function _verifyAuditAttestation(bytes32 attestationHash) internal view returns (bool);
// confirms an external auditor has signed off on newImplementation
function _verifyCodeHashMatches(address impl, bytes32 expectedHash)
internal view returns (bool);
function _migrateStorage(uint8 moduleKind, address oldImpl, address newImpl) internal;
// module-specific migration; reverts on storage incompatibility
3.4 Events
event UpgradeProposed(
uint8 indexed moduleKind,
address newImplementation,
bytes32 newCodeHash,
bytes32 auditAttestationHash,
uint64 activatesAt
);
event UpgradeVetoed(
uint8 indexed moduleKind,
address indexed guardian,
uint8 vetoKind // CONSTITUTIONAL, MODULE
);
event UpgradeActivated(
uint8 indexed moduleKind,
address oldImplementation,
address newImplementation
);
event UpgradeAborted(uint8 indexed moduleKind);
event GuardianRotated(
uint8 indexed moduleKind, // 255 for constitution guardian
address oldGuardian,
address newGuardian
);
3.5 Modifiers and access control
modifier onlyGovernance();
modifier onlyConstitutionGuardian();
modifier onlyModuleGuardian(uint8 moduleKind);
modifier upgradeReady(uint8 moduleKind);
4. Invariants
| # | Invariant |
|---|---|
| M6.1 | No upgrade without audit: proposeUpgrade reverts unless auditAttestationHash != 0 and _verifyAuditAttestation confirms a signed external auditor attestation. |
| M6.2 | Minimum delay: pendingActivatesAt - block.timestamp >= MIN_UPGRADE_DELAY (30 days). |
| M6.3 | Guardian window: guardianVetoEndsAt - pendingActivatesAt >= MIN_GUARDIAN_WINDOW (7 days). |
| M6.4 | Veto irreversible: once UpgradeVetoed emitted, the slot returns to pending == 0; new proposal required. |
| M6.5 | Storage compatibility: _migrateStorage reverts on incompatible layout; auditor must verify storage layout in advance. |
| M6.6 | Constitution-guardian scope: vetoConstitutional is restricted to violations enumerated in 00-foundations/03-binding-principles.md. |
| M6.7 | No circular upgrade: M6 cannot upgrade itself in the same proposal that upgrades another module (M6 self-upgrade is a separate, longer-delayed proposal class). |
| M6.8 | Code-hash binding: _verifyCodeHashMatches confirms newImplementation’s deployed bytecode hash equals newCodeHash recorded at proposal time, preventing post-proposal contract substitution. |
5. Threat model
| Threat | Mechanism | Mitigation |
|---|---|---|
| Malicious upgrade slipping in | Attacker drafts module that violates a bright line (introduces tradeable token, DeFi yield, cross-property primitive) | M6.1 audit requirement; M2 supermajority; constitution-guardian veto; bright-line list is public and enumerated |
| Storage corruption on upgrade | New module has incompatible storage layout, corrupting state | M6.5 storage migration check; auditor required to review storage layout |
| Code substitution post-proposal | Adversary swaps the contract at newImplementation between proposal and activation | M6.8 code-hash binding |
| Constitution-guardian compromise | Landseed-officer multi-sig compromised | Multi-sig threshold (e.g., 3-of-5); officer rotation per Foundation governance; backup guardian designation; (specific recovery procedure counsel-confirmation required) |
| Constitution-guardian unresponsiveness | Multi-sig signers offline | M6.3 fixed window; absence = clearance; emergency convening protocol off-chain |
| Guardian removal as coup | M2 supermajority votes to remove guardian | Guardian rotation requires M2 + 30-day delay + Foundation-level escalation; (Foundation-formation-dependent; not yet built) |
| Self-upgrade exploit | M6 upgrades itself to disable guardian | M6.7 separate-proposal-class requirement; longer delay (60 days minimum); Foundation oversight |
| Audit-attestation forgery | Fake audit hash submitted | _verifyAuditAttestation requires signed attestation from one of a registered auditor public-key set; auditor set governance-managed |
6. Per-template variations
| Template | Upgrade procedure | Notable |
|---|---|---|
| A, B, D, E, G — Tier 1 | OA amendment; signature-based; 30-day notice; constitution-guardian non-objection | No on-chain upgrade; M6 is OA structure only |
| C — Indigenous | Smart-contract upgrade with FPIC-gated M2 supermajority; cultural-guardian non-objection on culturally-relevant modules; 60-day notice | Per-deployment audit on each upgrade |
| F — Hybrid | Smart-contract upgrade with composed-stakeholder approvals; per-component guardian non-objections; 60-day notice | Per-deployment audit on each upgrade |
7. External dependencies
- External auditor public-key set — registered list; auditor signatures verified
- Foundation governance (not yet built) — long-term oversight of guardian rotation
- M2 — supermajority proposals
- All other modules — upgrade target
8. Audit-scoping notes
- Estimated audit hours: 80–110 hours for M6 standalone (high due to cross-module storage-migration verification)
- Required auditor expertise: Solidity; proxy and upgradeability patterns (UUPS, transparent proxy, beacon, diamond); storage-layout analysis; OpenZeppelin Upgrades familiarity; experience with timelock + guardian-veto patterns
- Highest-risk areas: storage-layout compatibility (M6.5); code-hash binding (M6.8); guardian-rotation paths; M6 self-upgrade path
- Test coverage required: 100% for
proposeUpgrade,activateUpgrade,vetoConstitutional,vetoModule; storage-migration property tests per module type; guardian-window timing tests - Audit-last dependency: M6 should be audited last in the initial library audit because it depends on the storage layouts and module interfaces of M1–M5 being stable
Per-template module composition table
The table below specifies, for each template, which modules are deployed in Tier 1 vs. Tier 2 form and the per-module configuration choices that the auditor will need to verify. Configuration parameters (specific percentages, weights, thresholds) are deployment-time inputs, not template properties.
| Template | M1 (Registry) | M2 (Governance) | M3 (Economics) | M4 (Mgmt Plan) | M5 (Attestation) | M6 (Upgrade) |
|---|---|---|---|---|---|---|
| A — Solo | Tier 1 (OA + multi-sig signers); landowner-primary + family + Landseed steward seats; OA inheritance succession | Tier 1 (OA procedures); landowner supermajority on use; Landseed methodology authority; methodology-guardian multi-sig | Tier 1 (OA + multi-sig disbursement); landowner 60–85%; stewardship reserve 5–25%; protocol fee 2–5%; cash only | Tier 1 (OA amendment); standard signature procedure | Tier 1 (manual recording in LLC books); per-attestation reference in distribution records | Tier 1 (OA amendment; 30-day notice; constitution-guardian non-objection) |
| B — Land Trust | Tier 1 (OA + multi-sig); land-trust-officer + Landseed steward + optional advisor; institutional succession | Tier 1 (OA + institutional board procedures); land-trust majority; methodology-guardian veto; two-key on material plan changes | Tier 1; land-trust 60–80%; reserve 10–25%; protocol fee 2–5% | Tier 1 (OA + institutional approval); coordinated with conservation easement | Tier 1 (manual recording) | Tier 1 (OA amendment) |
| C — Indigenous | Tier 2 (smart contract); community-council + cultural-guardian + elder + Landseed-steward seats; community-determined succession; permissioned read access | Tier 2; FPIC-gated supermajority; cultural-guardian non-objection on cultural class; methodology-guardian veto on destruction | Tier 2; community fund 40–80%; cultural-guardian discretionary 5–15%; stewardship 5–15%; protocol fee 2–5%; cash only | Tier 2 (content-addressed; FPIC-gated ratification); 60-day deliberation | Tier 2 (smart-contract listener); permissioned access; aggregated ECI public, raw obs. community-only | Tier 2; FPIC-gated supermajority on upgrade; cultural-guardian non-objection on culturally relevant modules; per-deployment audit |
| D — Corporate | Tier 1; corporate-officer + ESG observer + Landseed steward; corporate succession | Tier 1; corporate-board supermajority; ESG observer non-binding | Tier 1; corporate 70–90%; reserve 5–25%; protocol fee 2–5% | Tier 1; institutional approval + quarterly disclosure | Tier 1 (manual recording + quarterly disclosure) | Tier 1 (OA amendment) |
| E — Sovereign | Tier 1; sovereign-agency designee + local management + Landseed steward; agency succession; political-disruption fallback | Tier 1; sovereign residual on national-policy class; operational delegation; treaty-reporting hook | Tier 1; environmental fund 60–80%; local management 10–25%; protocol fee 2–5% | Tier 1; sovereign approval | Tier 1 (manual recording + treaty reporting) | Tier 1 (OA amendment per sovereign procedures) |
| F — Hybrid | Tier 2; composed seats from underlying templates; per-seat succession (per underlying template) | Tier 2; decision-class routing across composed seats; mediation-first dispute resolution; FPIC if C-component; cultural-guardian if C-component | Tier 2; composed distribution; protocol fee 2–5%; cash only | Tier 2 (content-addressed; composed approval); cross-component conflict resolution | Tier 2; cross-stakeholder access controls | Tier 2; composed-stakeholder approvals; per-deployment audit on every upgrade |
| G — Stewardship | Tier 1; foundation officers + Landseed steward; foundation succession | Tier 1; foundation procedures; methodology-guardian veto | Tier 1; all to stewardship reserve; protocol fee 2–5% | Tier 1; foundation approval | Tier 1 (manual recording) | Tier 1 (OA amendment) |
Templates F and G are flagged in 02-governance-templates/CLAUDE.md as deferred to per-deployment audit (current position). The initial library audit covers Templates A, B, C, D, E.
What’s NOT in the modules
These exclusions are template-level expressions of the bright lines in 04-perimeter/:
- No tradeable tokens. M1 enforces non-transferability (M1.1); M3 emits no token (M3.7).
- No public participation. M1 enforces permissioned membership (M1.7); admission requires governance proof.
- No DeFi interactions. M3 holds treasury in stablecoin only; no
lend,stake,swap, or external-protocol calls (M3.3). - No cross-DAO calls. Each property’s modules are isolated;
propertyId(M5.5) and per-propertyregistryContractconfiguration prevent contamination (M3.8). - No automated yield. Treasury management is conservative; only configured stablecoin held; rebalancing requires governance.
- No DAO credit issuance. M5 has no
mint(M5.1); the DAO consumes evidence of issuance, never produces it.
These properties must remain after every M6 upgrade. The constitution-guardian veto exists specifically to enforce this.
Audit-scoping summary
Total estimated audit hours — initial library audit
The initial library audit covers M1–M6 in the form deployed for Templates A through E (Templates F and G are deferred to per-deployment audits).
| Module | Hours | Audit-sequence position |
|---|---|---|
| M1 — Beneficiary Registry | 60–90 | First (foundation for M2/M3/M5) |
| M2 — Governance | 120–180 | Second (depends on M1) |
| M3 — Economics | 60–80 | Third (depends on M1; integrates with M5) |
| M4 — Management Plan | 30–50 | Fourth (depends on M2) |
| M5 — Attestation Hooks | 50–70 | Fifth (depends on registry interface; integrates with M3) |
| M6 — Upgrade Path | 80–110 | Last (depends on storage layouts of M1–M5 being stable) |
| Cross-module integration tests | 60–100 | After all modules independently audited |
| Bright-line verification (no tokens, no DeFi, no cross-property) | 30–50 | Final pass |
| Report writing + remediation review | 40–80 | Final |
| Total | 530–810 hours |
At a $400/hour blended audit rate, this is $210k–$325k for the initial library audit. This is consistent with the $200k Template C budget noted in 02-governance-templates/CLAUDE.md plus margin for the broader library scope.
Per-template incremental audit hours
After the library audit, each template deployment requires incremental audit work to verify deployment-specific configuration:
| Template | Incremental audit hours | Notes |
|---|---|---|
| A — Solo | 10–20 | Mostly OA review; multi-sig configuration check |
| B — Land Trust | 20–40 | OA review; institutional-procedure-mapping check; easement coordination review |
| C — Indigenous | 200–375 | Per-deployment partial bespoke build per templates/C-indigenous-co-design.md; FPIC encoding review; cultural-protocol encoding; permissioned-access controls; outside review by indigenous-rights advocate (separate budget) |
| D — Corporate | 30–50 | OA review; ESG-observer permission scope; quarterly disclosure mechanics |
| E — Sovereign | 40–80 | OA review; treaty-reporting integration; political-disruption fallback verification |
| F — Hybrid | 200–375 | Per-deployment audit required; composition-specific securities review; deadlock prevention verification; cross-stakeholder dispute path |
| G — Stewardship | 20–40 | OA review; foundation-procedure mapping |
Template C and F costs are partly bespoke. The library audit does not amortize fully across them because the deployment-specific composition is genuine, not parameterization.
Required auditor expertise
The audit team should collectively possess:
- Solidity / EVM — necessary for all module audits; ≥3 years deep experience
- Governance patterns — OpenZeppelin Governor, Aragon OSx, or Tally Governor; one of these strongly preferred (the implementation may use any; the auditor benefits from familiarity with the chosen toolchain). For M2 specifically.
- Proxy / upgradeability — UUPS, transparent, beacon, or diamond proxy patterns; storage-layout analysis. For M6.
- Access control — OpenZeppelin AccessControl, role-based modifiers, multi-sig integration. For M1, M2, M6.
- Signature verification — ECDSA, EIP-712, cross-chain attestation patterns. For M5.
- Reentrancy and atomicity — payment-router patterns; PaymentSplitter familiarity. For M3.
- Token standards (negative) — auditor must verify absence of ERC-20 / ERC-721 / ERC-1155 mint paths; experience with token standards required to know what to look for.
- Indigenous-rights review (not auditor expertise per se; separate review track) — for Template C, an indigenous-rights advocate must review FPIC encoding, cultural-guardian scope, and CARE-principle implementation in parallel with smart-contract audit.
Suggested audit firms (per templates/C-indigenous-co-design.md): Trail of Bits, OpenZeppelin, ConsenSys Diligence, Spearbit, or specialized DAO auditor with governance-pattern experience. Boutique firms acceptable for the initial library audit if they bring governance-pattern fluency.
Suggested audit sequence
- M1 Beneficiary Registry first. Foundation for all other modules’ state reads.
- M2 Governance second. The largest module; all execution paths flow through it.
- M3 Economics third. Smallest cross-module surface; can be audited in parallel with M4.
- M4 Management Plan fourth. Content-addressed, low complexity; can run parallel to M3.
- M5 Attestation Hooks fifth. Depends on registry interface stability.
- M6 Upgrade Path last. Requires M1–M5 storage layouts to be stable.
- Integration tests after all modules. Cross-module call paths; reentrancy across contract boundaries; bright-line verification (no tokens / no DeFi / no cross-property primitives).
- Per-template configuration review for the chosen pilot templates (A and B for Tier 1; C deferred per
02-governance-templates/CLAUDE.mdto a partnered deployment).
What’s NOT in the audit scope (deferred)
- Foundation buffer-pool contract audit — separate from per-property module audit; covered under registry/Foundation audit (per
05-interfaces/05-buffer-pool-specification.mdaudit budget) - Registry contract audit — separate; per
05-interfaces/04-registry-function-specification.md - Coalition entity contracts — Exchange, Fund; out of scope per
05-interfaces/CLAUDE.md(coalition entities are counterparties, not parts of the per-property vehicle) - NRD-lite legal-instrument review — not a smart-contract audit; counsel-led legal review per
01-nrd-lite/ - Templates F and G — deferred to per-deployment audits (current position per
02-governance-templates/CLAUDE.md); revisit if deployment frequency justifies library inclusion
Open audit-scoping questions
| Question | Owner | Status |
|---|---|---|
| Specific framework choice (Aragon vs. Tally vs. custom) | Engineering + counsel | Open; specification is framework-agnostic |
| L1 vs. L2 deployment chain | Engineering + counsel | Open; affects timestamp-attack and gas-cost analysis |
| Auditor selection | Landseed PBC + the co-architect | Open; shortlist in templates/C-indigenous-co-design.md |
| Storage layout finalization | Engineering | Required before M6 audit |
| Methodology-version-pinning data structure | Methodology stewards + engineering | Required before M5 audit |
| Wallet-auth pattern (Q9: hybrid custodial / hardware / paper) | Engineering + UX + counsel | Required before M1 audit; current position is hybrid per 06-risks/04-proposed-resolutions.md |
| Cross-chain or multi-chain considerations | Engineering | Open; bears on M5 signature verification |
| External auditor public-key registration (M6.1) | Engineering + counsel | Open; affects M6 audit-attestation verification |
These are execution-phase decisions that follow from the architecture. The architecture itself is settled at the level of this specification. (Specific framework choice deferred; counsel-confirmation required across regulatory dimensions.)
Cross-module consistency — phase-9.5 audit findings and resolutions
The following inconsistencies were surfaced by an internal cross-module audit at the close of the fourth iteration. Each is a specification-level fix that the implementation must respect; auditors should treat these as binding refinements to the per-module sections above.
Fix 1 — M3.10: bufferPoolFeeBps must be constant 0 (no governance setter)
The bufferPoolFeeBps state variable in M3 is declared uint256 public bufferPoolFeeBps with the documentation “0 here; buffer is taken pre-DAO at registry layer.” There must be no setter exposed to governance, no constructor-time value other than 0, and a new invariant M3.10: bufferPoolFeeBps == 0; enforced by deployment as constant; no governance path can change it. This prevents an inadvertent governance proposal from creating a DAO-side buffer fee on top of the registry-layer buffer.
Fix 2 — M3._verifyRegistryProof: M3 must independently validate the registry proof
M3’s recordInflow is gated by onlyAttestationHook (msg.sender == address(M5)). M3 also receives bytes calldata registryProof from M5 but does not validate it. Add internal function _verifyRegistryProof(bytes calldata registryProof) to M3 §3.3 that confirms the proof was signed by the registry’s attestor key (independently of M5’s prior validation). This closes the gap where an upgraded M5 could pass invalid proofs to M3 without M3 re-checking. M3.8 enforcement now reads: “via independent _verifyRegistryProof validation of registryProof parameter; M5’s prior validation is not relied upon.”
Fix 3 — M1 and M6 governanceProof parameter discipline
M1’s admitMember and M6’s proposeUpgrade accept bytes calldata governanceProof AND are gated by onlyGovernance. The governanceProof parameter is redundant if onlyGovernance enforces M2-only origin. Resolution: remove the governanceProof parameter from both functions. Access control is enforced solely by onlyGovernance. This simplifies the spec and eliminates an under-defined verification surface.
Fix 4 — M2 must publish ratifiedMethodologyVersions for M5 to consume
M5’s _verifyMethodologyPin references “the set of methodology versions ratified for this property” but M2 has no state variable, getter, or event documenting this set. Add to M2 §3.1: mapping(bytes32 => bool) public ratifiedMethodologyVersions populated by execution of ADOPT_METHODOLOGY proposals. Add public getter: function isMethodologyRatified(bytes32 version) external view returns (bool). M5 now calls this getter directly. The cross-module dependency is explicit, not implicit.
Fix 5 — M2 declares its own kycCurrent modifier; M1 exposes getMemberIdByHolder
M2’s castVote references kycCurrent(memberIdOfSender) but kycCurrent is defined only on M1. Resolution: declare a M2-local modifier kycCurrent(uint256 memberId) that calls M1.isKycCurrent(memberId). Also add to M1 §3.2: function isKycCurrent(uint256 memberId) external view returns (bool) and function getMemberIdByHolder(address holder) external view returns (uint256) (the latter exposes what is currently internal _holderToMemberId). M2’s onlyMember modifier and its castVote’s memberIdOfSender lookup now use the public getter.
Fix 6 — M5.setRegistryConfig: governance-controlled registry rotation
M5.8 invariant requires that registryContract and registryAttestorPubkeyHash changes require M2 supermajority + 30-day delay, but no setRegistryConfig function exists. Add to M5 §3.2: function setRegistryConfig(address newRegistry, bytes32 newAttestorPubkeyHash) external onlyGovernance with the 30-day delay enforced via M2’s standard DECISION_UPGRADE timelock. Emits the previously declared RegistryConfigChanged event. This closes the governance-control gap on the most security-critical M5 state.
Fix 7 — Emergency-path key rotation (Test 23 mitigation)
The 30-day delay on registry-attestor key rotation is too slow during force-majeure events. Add to M5 §3.2 a separate emergency path: function emergencyRotateAttestor(address newRegistry, bytes32 newAttestorPubkeyHash, bytes calldata foundationApproval) external with a 72-hour timelock instead of 30 days, gated by Foundation methodology stewards’ multi-sig (≥3 of 5) + audit committee non-objection. This is bounded by M5.8.E (new invariant): emergency rotation requires Foundation multi-sig + audit committee + ≥48-hour public notice; cannot reduce timelock below 72 hours.
Fix 8 — M4 chain integrity: dual storageUri (Attack A-5 mitigation)
M4’s proposePlanVersion accepts a single storageUri. Adversary substitution at the URI source is detectable but creates a governance race window. Add to M4 §3.1: PlanVersion.storageUriPrimary and PlanVersion.storageUriBackup (both content-addressed). Ratification must verify content-hash from BOTH URIs before activation. M4.7 (new invariant): ratification requires byte-equal content from primary AND backup URIs; otherwise revert.
Fix 9 — Replacement-credit receipt flagging (Attack A-12 mitigation)
Cross-spec gap: buffer pool replacement credits could double-count revenue if M5/M3 treat them as new attestations. Resolution: buffer pool reversal generates attestation receipts with explicit flag isReplacement: true and replacementFor: bytes32 (original receipt hash). M5 §3.1 adds mapping(bytes32 => bytes32) public replacementOf populated when a replacement attestation is consumed. M3 §3.3 adds invariant: if replacementOf[receiptHash] != 0, recordInflow reverts with REPLACEMENT_NO_REVENUE — replacement credits do not generate fresh revenue distribution. Coordinated update with 05-interfaces/05-buffer-pool-specification.md reversal-process step 5.
Fix 10 — Cross-module call/event matrix (canonical reference)
The canonical cross-module dependency graph after the above fixes:
| Caller / Emitter | Target | Mechanism |
|---|---|---|
| M1 → M2 | governance reads voting weight | M2 calls M1.getVotingWeight(memberId, decisionClass) (public getter) |
| M1 → M3 | distribution share | M3 calls M1.getDistributionShareBps(memberId) at addRecipient time |
| M1 → M2 | KYC currency | M2 calls M1.isKycCurrent(memberId) (Fix 5) |
| M1 → M2 | member resolution | M2 calls M1.getMemberIdByHolder(holder) (Fix 5) |
| M2 → M1 | execution | M2 calls M1.admitMember, M1.removeMember, M1.setSuccessorRoot (Fix 3 — no governanceProof param) |
| M2 → M3 | execution | M2 calls M3.addRecipient, M3.updateRecipientShare, etc. |
| M2 → M4 | execution | M2 calls M4.ratifyPlanVersion, M4.activatePlanVersion, M4.revokePlanVersion |
| M2 → M5 | registry config | M2 calls M5.setRegistryConfig (Fix 6) |
| M2 → M6 | execution | M2 calls M6.proposeUpgrade (Fix 3 — no governanceProof param) |
| M2 → M5 | methodology ratification | M5 calls M2.isMethodologyRatified(version) (Fix 4) |
| M5 → M3 | revenue | M5 calls M3.recordInflow(amount, receiptHash, registryProof); M3 independently validates registryProof (Fix 2) |
| Registry → M5 | attestation lifecycle | Registry calls M5.notifyIssuance, M5.notifyRevenue, M5.notifyInvalidation |
| Registry → M5 | replacement credit | Registry calls M5.notifyReplacement(receiptHash, replacementFor) (Fix 9) |
| M6 → M1–M5 | upgrade activation | M6 swaps module addresses; storage migration follows M6 invariants |
What this means for audit scoping
The 530–810 hours estimate from the per-module summary remains valid post-fixes. The fixes are surgical clarifications that strengthen the audit posture without expanding scope. M5 sees the largest delta (Fix 6, Fix 7, Fix 9) — auditor should add ~20 hours for verification of the registry-config rotation paths and replacement-credit flagging logic. M3 sees a smaller delta (Fix 1, Fix 2, Fix 9) — add ~10 hours. Net audit-hour increase: ~30 hours; revised total 560–840 hours for the initial library audit.
These fixes are mandatory before the M5 audit and the M3 audit. M1, M2, M4, M6 fixes (Fix 3, Fix 4, Fix 5, Fix 8) are mandatory before their respective module audits.