Corngate is built as two strictly separated layers:
- Passport layer (infrastructure) — identity, evidence, claims, verification, history and blockchain certification for land, trees and farms. This is what v0.1 ships.
- Application layer (regulated) — lending, insurance, leasing, Corngate Grow (fractional farming) and carbon. Each application is separately governed and gated. In v0.1 only two application surfaces exist, both non-financial: Founding Forest sponsorship (recognition only) and the Grow waitlist (no investment).
A Passport NFT is a certificate of a registry record. It is not legal title and not an investment. That boundary is enforced in copy, schema constraints, feature flags and the contract design — not just in a disclaimer.
System overview
Diagram — rendered in the repository; source shown here
flowchart TB
subgraph Public["Public web (Next.js 15 App Router, Vercel)"]
Home["/ homepage"]
Reg["/registry map + search"]
PP["/passport/{land|tree|farm}/[id]"]
FF["/founding-forest"]
Grow["/grow (waitlist only)"]
end
subgraph Workspaces["Authenticated workspaces (Phase 3)"]
Dash["/dashboard registrant"]
Ver["/verify verifier queue"]
Adm["/admin registry admin"]
Inst["institutional data room"]
end
subgraph Data["Supabase"]
PG[("Postgres + PostGIS<br/>Passport Engine (RLS)")]
Auth["Auth + roles"]
Store["Private storage<br/>(evidence files, signed URLs)"]
end
subgraph Chain["Certification (Phase 5)"]
SC["CorngatePassportRegistry<br/>ERC-721 · Polygon Amoy testnet<br/>transfers disabled · mainnet OFF"]
IPFS["IPFS/Pinata<br/>public metadata only"]
end
Public -->|"public views only<br/>(generalised geometry)"| PG
Workspaces --> Auth --> PG
Workspaces --> Store
Adm -->|"issue/revoke (explicit action)"| SC
SC --> IPFS
PG -.->|"source of truth"| SCThe database is the operational source of truth. The chain anchors selected public assertions; it never leads.
v0.1 demo-resilience note
Phase 1–2 (current) serves the seeded pilot registry from typed fixtures (src/lib/data/fixtures/) behind the same data-access signatures Supabase will use (src/lib/data/passports.ts). The public site therefore renders fully with zero external services, tokens or keys. Phase 3 swaps the implementation, not the pages. (ADR-004.)
Passport Engine — entity relationships
Diagram — rendered in the repository; source shown here
erDiagram
PROFILES ||--o{ USER_ROLES : holds
PROFILES ||--o{ DATA_ROOM_GRANTS : receives
PARTIES ||--o{ CLAIMS : named_in
PASSPORTS ||--o| LAND_DETAILS : "land extension"
PASSPORTS ||--o| TREE_DETAILS : "tree extension"
PASSPORTS ||--o| FARM_DETAILS : "farm extension"
PASSPORTS ||--o{ CLAIMS : has
PASSPORTS ||--o{ EVIDENCE : indexes
PASSPORTS ||--o{ INSPECTIONS : logs
PASSPORTS ||--o{ PASSPORT_VERSIONS : versions
PASSPORTS ||--o{ DISPUTES : subject_of
PASSPORTS ||--o{ CERTIFICATES : certified_by
PASSPORTS ||--o{ SPONSORSHIPS : "trees only"
FARM_PARCELS }o--|| PASSPORTS : farm
FARM_PARCELS }o--|| PASSPORTS : land
TREE_DETAILS }o--o| PASSPORTS : "on parcel"
CLAIMS }o--o{ EVIDENCE : supported_by
SPONSORSHIPS }o--o| PAYMENTS : paid_via
PASSPORTS {
uuid id PK
text registry_ref UK "CG-LND-0001"
enum asset_type "land|tree|farm"
enum status "6-state workflow"
geometry approx_centroid "public, ~1km"
bool is_public
}
CLAIMS {
enum role "9 roles incl. customary"
enum status "own verification state"
date effective_from
uuid supersedes_claim_id
}
EVIDENCE {
enum kind
enum visibility "public|permissioned|private"
text sha256 "integrity hash"
text storage_path "private bucket"
}
CERTIFICATES {
enum state "not_issued|simulated|issued|revoked"
bool simulated "no tx hash allowed if true"
text network "polygon-amoy ONLY (check)"
text token_uri_cid
}Full DDL with row-level security: supabase/migrations/0001_passport_engine.sql.
Core design rules
- R1 — Claims, not tokens. Ownership and every other person↔asset relationship is a
claimsrow: role (legal owner, customary interest holder, lessee, operator, custodian, beneficiary, surveyor, verifier, sponsor), its own verification status, evidence links, effective dates, supersession chain. Exact legal conclusions are recorded outcomes of human/institutional verification — the system never infers them. - R2 — Private by default. Precise boundaries/points and personal documents live in columns/buckets no public policy can reach. Public surfaces read
registry_public(generalised geometry) only. Seedocs/DATA_CLASSIFICATION.md. - R3 — Append-only history. Versions, inspections, disputes and audit rows are never deleted. Records are superseded or revoked with reasons, and revoked/superseded records remain publicly inspectable (see CG-TRE-0013, CG-TRE-0005 in the seed data).
- R4 — Honest certification. A certificate row is
simulateduntil a real Amoy transaction exists; a DB constraint forbids a simulated certificate from carrying a tx hash, and the UI labels simulation explicitly. Mainnet is excluded by acheckconstraint — enabling it is a deliberate migration, i.e. a founder-visible act. - R5 — No investment plumbing. No table stores fractional units, balances or distributions. Grow stops at a waitlist with jurisdiction + "no offer" acknowledgement. The
flags.GROW_INVESTMENTgate is false and v0.1 contains no code path it could enable.
Verification workflow
Diagram — rendered in the repository; source shown here
stateDiagram-v2
[*] --> unverified_claim : registration intake
unverified_claim --> document_verified : verifier accepts documents
document_verified --> field_verified : field inspection passes
unverified_claim --> disputed : dispute opened
document_verified --> disputed
field_verified --> disputed
disputed --> document_verified : resolved (documents stand)
disputed --> field_verified : resolved (field state stands)
unverified_claim --> superseded : duplicate/corrected record
document_verified --> superseded
field_verified --> superseded
document_verified --> revoked : fraud/material error
field_verified --> revoked
disputed --> revokedCertificates are issued only from document_verified/field_verified with no open dispute; a dispute pauses certificate actions; revocation of a passport revokes its certificate.
Repository layout
corngate-platform/
├─ docs/ architecture, decisions, status, boundaries, threat model
├─ supabase/migrations/ Passport Engine DDL + RLS
├─ contracts/ Solidity (Phase 5): CorngatePassportRegistry
├─ src/
│ ├─ app/ App Router routes (see docs/ROUTES.md)
│ ├─ components/ design system + passport/registry components
│ └─ lib/
│ ├─ data/ types, fixtures (seed registry), access layer
│ └─ flags.ts regulatory/product feature gates
└─ .env.example every secret the platform will ever need, documented
Stack
Next.js 15 App Router + TypeScript strict · Tailwind v4 design tokens · Supabase (Postgres/PostGIS/Auth/Storage/RLS) · Mapbox GL JS (token-gated; schematic SVG fallback built in) · Solidity + OpenZeppelin ERC-721/AccessControl/Pausable on Polygon Amoy via Hardhat + viem · Pinata/IPFS · payment adapter interface with mock mode (no hard-coded provider) · Zod + React Hook Form · Vitest + Playwright · Vercel + GitHub Actions.
