Ecosystem Climate Capital /Technical Documentation

Engineering

Backend, Security & Data Access

Server-side architecture, security posture, and data-access model for the Nordic Climate Finance Hub Investor Platform. Optimised for investor-grade due diligence: confidentiality, auditability, and least-privilege access to sensitive deal information.

Design principles

  • Confidentiality by default — every record is private until an explicit grant says otherwise.
  • Least privilege — every actor (user, service, integration) gets the minimum scope they need.
  • Defence in depth — controls layered across network, application, database, and key-management planes.
  • Audit everything — every access, decision, and export is recorded in an immutable log.
  • EU data residency — primary storage in the EU; cross-border replication is opt-in per tenant.

Stack

  • Runtime: TanStack Start server functions on an edge worker runtime; TypeScript strict mode.
  • Database: Managed Postgres with row-level security (RLS) enforced on every table.
  • Auth: OIDC / SAML SSO for investor tenants; short-lived JWT bearer tokens; SCIM provisioning.
  • Object storage: Encrypted bucket with signed, expiring URLs for every upload and download.
  • Secrets: Managed secret store; no secrets in code, env files, or client bundles.
  • Validation: Zod at every trust boundary (request, server-fn input, webhook payload, env).

Identity & access

  • Authentication: SSO (SAML / OIDC) for investor firms; email + password with mandatory MFA for individual members; hardware-key (WebAuthn) supported for partner / admin roles.
  • Session policy: short-lived access tokens, refresh rotation, idle timeout, device binding for admin sessions.
  • Provisioning: SCIM 2.0 for joiners, movers, and leavers; deprovisioning revokes sessions within 60 seconds.
  • Roles: admin, partner, analyst, observer, sponsor; roles stored in a dedicated user_roles table, never on the profile.
  • Authorization: RLS policies use a security definer has_role() function to avoid recursive checks.

Data access model

Access is evaluated in three layers. A request must satisfy all three to read or write a record.

LayerEnforcesMechanism
TenantInvestor firm isolationtenant_id on every row; RLS predicate matches the caller's tenant.
EntitlementWhich opportunities / data rooms the tenant can seeentitlements table with explicit grants per opportunity, instrument, or DD workstream.
RoleWhat the caller can do once they can see itRole check via has_role(auth.uid(), …); mutating operations gated server-side.
  • Data rooms require an active NDA acceptance and a non-expired grant; revocation invalidates signed URLs within 60 seconds.
  • Cross-tenant joins are forbidden at the database layer; aggregate views (leaderboards, stats) are materialised through privacy-preserving server functions, never direct table access.
  • Service role is used only by trusted server-side admin operations (backfills, webhooks) — never reachable from client code.
  • Read paths default to the authenticated Supabase client so RLS applies as the caller; admin client is reserved for verified, server-only flows.

Encryption & key management

  • In transit: TLS 1.3 only; HSTS preloaded; modern cipher suites; certificate pinning on mobile.
  • At rest: AES-256 on database and object storage; envelope encryption for highly sensitive fields (KYC, beneficial ownership, banking).
  • Key management: managed KMS with per-tenant data keys; automatic rotation; access logged.
  • Backups: encrypted, geo-redundant within the EU; quarterly restore drills.

Confidentiality controls for documents

  • Per-tenant, per-data-room signed URLs with short TTLs (default 5 minutes).
  • Dynamic, per-user watermarking (name, email, timestamp, IP) on PDF and image previews.
  • NDA gating: an unsigned NDA blocks all document access for that opportunity.
  • Download controls per role; observers default to view-only with print disabled.
  • Full access log: who viewed which document, when, from where, for how long.

Audit & immutability

  • Append-only audit_log table covering authentication, access, mutation, decision, and export events.
  • Hash-chained entries; daily Merkle-root anchored to internal storage for tamper evidence.
  • IC decisions, NDA acceptances, and DD sign-offs are hash-stamped and immutable.
  • Closed reporting periods cannot be edited; restatements use append-only deltas.

Input validation & abuse prevention

  • Zod schemas validate every server-fn input and webhook payload — type, length, format, range.
  • Rate limiting on auth, search, document download, and export endpoints; per-tenant and per-IP buckets.
  • Webhook signature verification with constant-time compare; replay protection via timestamp + nonce.
  • Strict CORS allow-list; CSP with no inline scripts; SameSite=Strict session cookies.
  • File uploads scoped by MIME and size; antivirus scan before the file becomes accessible.

API conventions

  • Internal calls: typed server functions via createServerFn, gated by auth middleware.
  • External calls: REST + JSON; resource-oriented URLs; cursor pagination; RFC 7807 errors.
  • Versioning: URI versioning (/v1/…); breaking changes ship a new major.
  • Idempotency: Idempotency-Key required on POSTs that change state (invites, grants, transactions).

Example error

{
  "type": "https://errors.ncfhub.org/forbidden",
  "title": "Access not granted",
  "status": 403,
  "code": "dataroom.not_entitled",
  "detail": "Tenant TEN_18f has no active grant for opportunity OPP_9c3.",
  "instance": "/v1/datarooms/OPP_9c3/files"
}

Compliance posture

  • GDPR-aligned: lawful basis recorded per processing activity; DPIA on file for diligence data; DSR (access / erasure) workflows.
  • ISO 27001 control framework adopted; SOC 2 Type II on the roadmap.
  • Vendor due diligence on all sub-processors; sub-processor list published and versioned.
  • Pen tests at least annually and after major releases; findings tracked to closure with SLAs by severity.
  • Disclosed responsible-disclosure programme with a published security contact.

Out of scope

  • MRV data ingestion or carbon-credit registry integrations.
  • Payments, custody, or settlement of investor capital.
  • Field data capture from sensors or mobile measurements.