Skip to content
Corngate

Documentation centre

Architecture

System design, the Passport Engine schema and the verification workflow.

Rendered from docs/ARCHITECTURE.md in the repository — the file is the source of truth. All documents

Corngate is built as two strictly separated layers:

  1. Passport layer (infrastructure) — identity, evidence, claims, verification, history and blockchain certification for land, trees and farms. This is what v0.1 ships.
  2. 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"| SC

The 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 claims row: 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. See docs/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 simulated until 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 a check constraint — 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_INVESTMENT gate 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 --> revoked

Certificates 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.