TrueNorth

Reference architecture

Zero-egress analytics

Analyze sensitive fund and portfolio data. Nothing leaves.

30-second teaser

Skip to the blueprint

The file leaves the building.

Caption. Beat 1 of 5. An analyst exports a cap table or portfolio KPIs to a laptop, pastes them into a public AI tool, or emails them to a co-investor. The data flies out.

Reference architecture. TrueNorth does not run this, and it is not a certification.

Text version
  1. An analyst exports a cap table or portfolio KPIs to a laptop, pastes them into a public AI tool, or emails them to a co-investor. The data flies out.
  2. The cost is LP trust, deal leakage, and regulatory exposure.
  3. The data stays sealed in the vault. Only pixels and an approved aggregate leave.
  4. The team still models, uses AI, and writes the IC memo inside the boundary. Every export is signed and logged.
  5. See the blueprint, or scope a four-week pilot of this reference design.

A design for confidential firm data, portfolio-company metrics, and the proprietary files a diligence team uses. How we would build it.

This page is a reference architecture: how we would design zero-egress analytics, and the standard we build toward. TrueNorth does not run this system. There are no Nitro enclaves in the product, and TrueNorth is not certified for IL5, IL6, HIPAA, or FINRA. Names of those frameworks mean the design is written to align with the controls these frameworks require.

Decision brief

One screen, before the architecture.

The problem

Fund data leaves through laptops, email, data rooms, and AI tools. A workbook of portfolio metrics, an LP list, or a diligence file gets copied onto an endpoint, forwarded, or pasted into a public model. The question the team needed answered is reasonable. The copy that remains is the problem.

What a leak costs

A leak costs LP trust, regulatory exposure, and deal leakage. An LP who learns their identity sat in someone’s downloads reconsiders the relationship. Books-and-records and privacy rules care that the file left. A term sheet or a company metric in the wrong inbox changes a negotiation. This page does not put a dollar figure on that. The harm is the relationship, the obligation, and the deal.

  1. Status quo

    Files live on laptops and in inboxes. Access is a shared folder and a hope that people delete the copy.

  2. Buy

    A data clean room for a defined question with another party, or a virtual data room for document review. Each product covers a slice of the job.

  3. Build

    This blueprint: the firm’s own data stays in a private boundary, analysts work in a viewing desktop, and anything that leaves is a checked, signed aggregate.

Recommendation

For a fund that analyzes its own confidential book, and must keep the modeling inside a boundary, build toward this blueprint. Buy a clean room or a virtual data room when the job is a single shared question or a document review. Start with a four-week pilot on one dataset before committing to enclaves and signing.

Pilot: S. Governed catalog plus a viewing desktop for the book: M. Enclaves and signing: L. Privacy budgets and an in-boundary model: L. Tiers describe scope, not a price.

Threat model

Who would take the data, and what still gets through.

  • Departing employee

    Scenario

    Copies the book onto a laptop in the last week.

    Control

    The laptop never receives the file. Work happens in the viewing desktop. Exports are approved and watermarked.

    Residual risk

    What they already saw, and anything they photographed. The design does not erase memory.

  • Careless analyst

    Scenario

    Forwards a spreadsheet, or drops it in chat.

    Control

    No local file and no clipboard. A permitted export is a signed aggregate, not the raw extract.

    Residual risk

    A photograph or OCR of the screen. The watermark names them. It does not stop the picture.

  • Staff using public AI

    Scenario

    Pastes LP names or company metrics into a public model.

    Control

    The viewing network blocks public AI APIs. The only model endpoint is inside the boundary, behind the same masking.

    Residual risk

    A personal phone on another network, outside the desktop. The boundary does not cover devices it does not host.

  • Compromised laptop

    Scenario

    Malware on the endpoint reads local files and the clipboard.

    Control

    Rows are not on the endpoint. Credentials are short-lived. The session shows pixels.

    Residual risk

    A live session can still be watched while the person is signed in, including by a camera.

  • Co-investor or vendor

    Scenario

    A shared room or a vendor export becomes a second copy of the book.

    Control

    Share a checked aggregate or a watermarked view, not the raw room, unless an exception is approved.

    Residual risk

    A file an approver already released. The recipient can keep that file.

  • Malicious insider with clearance

    Scenario

    The role is allowed to see the cells, and the person intends to take them.

    Control

    Masking hides only what that role is not cleared for. The query, the signature, and the watermark record who looked.

    Residual risk

    A cleared insider can read what the role allows, including by photographing it. Clearance is not stopped by the gate.

  • External attacker

    Scenario

    Stolen keys or a direct read of the bucket from outside the network.

    Control

    Reads must come through the VPC endpoint. Analytical roles cannot write. The signing key is not the data key.

    Residual risk

    A stolen enclave role can ask what that role is allowed to read, until the session is revoked.

The design does not stop three things. A photograph of the screen, or OCR of that photograph. A cleared insider reading what their role is allowed to see. Collusion between two cleared people who compare what each was shown. Watermarks and the signed log make those acts traceable. They do not make them impossible.

Inference and differencing

A gate on one query is not a gate on the series.

A per-query rule of k ≥ 10 can be bypassed by subtraction. One query releases fintech with 18 companies. A later query asks for fintech without one named company. If that second group is still large enough to pass, the difference between the two averages points at the company that was left out. Two overlapping groups can do the same even when each group, alone, looks safe.

  • A query auditor compares a new cohort with recent cohorts from the same analyst and refuses a query whose overlap would support differencing.
  • A privacy budget per analyst and per dataset. An illustrative budget, not a setting for a real fund, is ε = 1.0 per analyst per dataset per quarter. A tighter illustrative alternative is ε = 0.1. Smaller ε adds more noise. Neither number is a recommendation.
  • Minimum cohort size and noise on the released aggregate. Suppression alone is the k ≥ 10 rule. Noise is the separate differential-privacy control.
  • Rate limits on how many queries an analyst can run against the same dataset in a day, so a differencing series cannot be walked quickly.

Analyst workflow

The friction is the point.

What pixel-only rules out

Pixel-only rules out local Excel. An associate cannot pull the extract onto a laptop and build the model there. That friction is the control. The blueprint replaces the local file with a workspace that stays inside the boundary.

In-boundary modeling

In-boundary modeling is a hosted spreadsheet or a notebook on the virtual desktop. The engine, the sheet, and the scratch work sit in that session. Nothing in that session is a file the laptop can save.

Permitted exports

Permitted exports are aggregates that have passed k-anonymity and the privacy budget. They leave as signed files with the provenance record, or as watermarked PDFs for an investment-committee memo. An exception, anything broader than that, waits for an approval workflow: the analyst requests, compliance reviews, and the export is logged.

Break-glass

Break-glass is a time-boxed exception for a named person and a named dataset. Two approvers are required, one from compliance and one from security. The session is watermarked, written to the audit log, and it expires. It is not a standing admin login.

A day in the life of an associate preparing an IC memo

  1. Morning. The associate opens the virtual desktop, not a local workbook, and submits the vintage benchmark.
  2. The catalog masks legal names. The climate group of three is rejected. The fintech group of 18 passes the gate.
  3. They model that aggregate in the hosted sheet inside the desktop. The rejected group is not on the screen.
  4. They request a watermarked PDF for the memo. The file carries the signature, the row count, and their identity.
  5. Compliance sees the signed record before the PDF is released. The memo cites that signature. The laptop never holds the parquet.

AI inside the boundary

The model sits where the query sits.

A public chatbot is an exit. The blueprint blocks that exit and, if a model is used at all, puts it behind the same catalog as SQL.

Network boundary

  • Compute sits in private subnets. There is no internet gateway and no NAT gateway.
  • S3 is reached through a VPC gateway endpoint. The bucket policy rejects a request that did not arrive through that endpoint.
  • DNS and network firewalls default to deny. They block public model APIs, including endpoints such as public OpenAI, Anthropic, and the Bedrock runtime API, unless a specific AWS endpoint is on the allow list.
  • KMS, STS, and logging are reached through VPC interface endpoints, not the public internet.

The safe pattern

  • The model endpoint lives inside the VPC, behind the same catalog and the same masking. A prompt can only see cells the role could have queried.
  • Prompts and outputs are logged and signed with the same provenance record as a SQL query.
  • Firm data is not used to train the model.
  • An aggregate the model produces still passes the k-anonymity gate and the privacy budget before anyone sees it.

Build vs buy

Different products, different jobs.

No prices. Effort is a tier. A clean room and a virtual data room are real options for a narrower job than this blueprint.

  • Protection

    This blueprint
    The firm’s own data stays in a private network. Analysts get pixels or a checked, signed aggregate.
    AWS Clean Rooms
    Two or more parties run an agreed analysis without exchanging raw tables.
    Snowflake data clean rooms
    Governed multi-party queries when the data already lives in Snowflake.
    Virtual data rooms
    Document viewing, watermarking, and an access log. Download is a setting, and many rooms still allow it.
    Confidential-compute platforms
    Hardware isolation of a workload. Catalog, egress, and export policy are still yours to design.
  • Analyst usability

    This blueprint
    High friction. No local spreadsheet. Modeling happens in the virtual desktop.
    AWS Clean Rooms
    A defined collaboration query, not a general workspace for the book.
    Snowflake data clean rooms
    Familiar if the team already works in SQL on Snowflake.
    Virtual data rooms
    Familiar for diligence document review.
    Confidential-compute platforms
    Whatever workflow you build on top of the isolated compute.
  • Effort

    This blueprint
    S for a four-week pilot on one dataset. L for the full design.
    AWS Clean Rooms
    M to stand up a room for one sharing question.
    Snowflake data clean rooms
    M when the warehouse is already Snowflake.
    Virtual data rooms
    S to M to open a room and set permissions.
    Confidential-compute platforms
    M to L. The platform is the isolation layer, not the fund’s process.
  • Ongoing operations

    This blueprint
    You run masking rules, the SIEM, key administration, and export approvals.
    AWS Clean Rooms
    The provider runs the room. You own the data and the analysis rules.
    Snowflake data clean rooms
    The provider runs the platform. You own grants and what may be released.
    Virtual data rooms
    The vendor runs the room. You own who may open which document.
    Confidential-compute platforms
    You run the workload and the policy around it.
  • Fit for funds

    This blueprint
    Internal analysis of portfolio, LP, and diligence data that must not leave.
    AWS Clean Rooms
    A specific question shared with a co-investor or an LP.
    Snowflake data clean rooms
    The same kind of shared question, on Snowflake.
    Virtual data rooms
    A traditional diligence room for documents.
    Confidential-compute platforms
    When you need attested compute and will build the catalog and the viewport yourself.

Implementation and operating model

Four phases, three roles, named approvers.

  1. Phase 0 · effort S

    Pilot, about four weeks, one dataset

    One portfolio extract. Masking on legal names. A virtual desktop for two or three associates. No enclave yet. The question to answer: can the team finish a real memo without a local file?

  2. Phase 1 · effort M

    Governed catalog and viewing desktop

    Lake Formation or Ranger covers the book. Row filters follow fund and role. The desktop is the only place the book is opened.

  3. Phase 2 · effort L

    Enclaves and signing

    The query moves into a Nitro Enclave. Provenance is signed inside KMS. The audit bucket is locked for seven years.

  4. Phase 3 · effort L

    Privacy budgets and an in-boundary model

    Overlapping queries are audited. Each analyst has a budget. A model endpoint, if you add one, sits behind the same gate.

Roles

  • Cloud and security engineer: network, KMS, enclave image, SIEM.
  • Data engineer: catalog, masking rules, the datasets the pilot is allowed to touch.
  • Compliance owner: what may be released, who may see which cells, and the export log.

Who decides

R is responsible, A is accountable, C is consulted, I is informed.

  • Masking rules

    Responsible
    Data engineer
    Accountable
    Compliance owner
    Consulted
    Security
    Informed
    GP
  • SIEM

    Responsible
    Security engineer
    Accountable
    Security engineer
    Consulted
    Compliance owner
    Informed
    COO
  • Export exceptions

    Responsible
    Analyst
    Accountable
    Compliance owner
    Consulted
    Security
    Informed
    GP or COO
  • Sign-off on a permitted export

    Responsible
    Analyst
    Accountable
    Compliance owner
    Consulted
    Security
    Informed
    Deal team

Incident response

One response for each SIEM trigger. These are operating steps for the on-call team.

k-anonymity rejection (N < 10)

Log the principal and the query hash. Do not release the group. A repeat from the same person in a short window is treated as a possible differencing attempt and raised from medium to high.

Memory dump or ptrace

Terminate the enclave and revoke the session. Security opens the host log. The dataset stays offline until the image is rebuilt.

Non-VPC S3 access

Severity 1. Page the on-call security engineer. Confirm the request was denied. Review the principal and freeze it if the call was not a known test.

KMS verification failure

Quarantine the exported artifact. Do not send it to the memo pack. Compliance and security compare the file with the audit record before anyone reissues it.

kms:Sign by anyone other than the enclave role

Disable that principal. Review the key policy. Signatures from the wrong role are not used as provenance.

Attempt to change the audit bucket lock

Alert the security owner. Compliance-mode retention is what blocks deletion. Treat the attempt itself as an incident.

Signing key scheduled for deletion or disabled

Cancel the deletion if it is still in the waiting period. Escalate. Old public keys have to stay verifiable for the retention window.

Blocked egress DNS query

Isolate the workload that asked. Investigate before it is allowed to query data again. A blocked name is a lead, not a success to ignore.

Architecture

How a question moves, and what is allowed to leave.

Encrypted storage and the governance catalog sit inside the trust boundary. Both feed an ephemeral enclave: a query engine, a k-anonymity gate, then a signature made inside a hardware security module. The enclave is Nitro isolation, not the viewing desktop. AppStream or WorkSpaces is the VDI outside that compute. The arrow out of the boundary is pixels only.

  • Boundary · Where raw data may exist
  • Data · Encrypted storage
  • Compute · Ephemeral enclave
  • Control · Masking, k-anonymity, signing
  • Egress · Pixels, and nothing else

The analyst sits outside the boundary, in neutral. Tap a block to open its details. Arrow keys move between blocks.

S3, encrypted. Holds the firm’s confidential objects: portfolio metrics, LP records, and diligence files. Analytical roles can read through the catalog. They cannot write new objects.

Play the query

A fictional diligence question walking the blueprint. The rows are made up. This is not a live query, and TrueNorth does not run this path today.

Press Play the query. You will see a masked identifier, a group rejected because N is under 10, an accepted aggregate, an asymmetric signature, a diagonally watermarked viewport, an auditor check, and the SIEM triggers.

Read each block

Boundary

Trust boundary

Raw records stay inside

What it does
Marks the line raw fund, portfolio, and diligence records are not allowed to cross. A person outside it can ask a question and see pixels. They do not receive a file.
Technology
A dedicated VPC in the design. Storage, the catalog, and the enclave sit inside it. The only designed exit is the pixel stream.
Mandate
Zero data egress. This is a logical boundary in the blueprint, not a claim that a network is physically air-gapped, and not a description of TrueNorth hosting today.

Outside

Analyst

Outside the boundary

What it does
A GP, principal, or diligence associate submits ANSI SQL. Their laptop is outside the trust boundary, so it is not a place the design will store an answer.
Technology
The query arrives as text. Identity is a short-lived credential, an hour at most, from a hardware-backed certificate.
Mandate
No long-lived API keys. The person is authenticated; the workstation is not trusted with raw rows.

Data

S3, encrypted

SSE-KMS, rotated keys

What it does
Holds the firm’s confidential objects: portfolio metrics, LP records, and diligence files. Analytical roles can read through the catalog. They cannot write new objects.
Technology
AWS S3 with a customer-managed KMS key, automatic rotation, in a FIPS 140-3 Level 3 HSM. The bucket policy allows TLS 1.3 only and denies s3:PutObject to analytical roles.
Mandate
Data at rest stays encrypted. Keys rotate. Analysts do not get a path that writes raw data back out.

Control

Governance catalog

Masking, RLAC, audit

What it does
Applies cell masking and row filters before a query can read. An LP legal name and a portfolio-company name become tokens. An account number comes back redacted. The query itself is logged.
Technology
AWS Lake Formation or Apache Ranger. Cell-level and row-level access, dynamic masking, and an immutable query audit entry.
Mandate
Cell-level access control. Each role sees the minimum cells that role needs for the question in front of it.

Compute

Confidential enclave

Ephemeral, attested

What it does
Runs the query in hardware-isolated memory that is discarded when the job ends. The host operating system is not a reader of that memory.
Technology
A Nitro Enclave in this design. The Nitro Hypervisor isolates its memory from the parent instance. That isolation is not AMD SEV and not Intel SGX. Attestation is a Nitro Enclaves document with PCR measurements, signed by the Nitro Hypervisor. NitroTPM measures the parent instance, not the enclave. A confidential VM, where one is used instead, would be AMD SEV-SNP or Intel TDX. Intel SGX is deprecated on many current processors and is not a target. No swap, no core dumps, no SSH, no persistent disk. The analyst’s desktop is a separate VDI, not this enclave.
Mandate
Zero raw local disk writes. Processing stays in encrypted, ephemeral RAM.

Compute

Query engine

DuckDB or Trino, RAM only

What it does
Executes the SQL against the stream the catalog permitted. The working set lives in volatile memory inside the enclave.
Technology
DuckDB or Trino, running only inside the enclave. No spill to local disk.
Mandate
The engine cannot be reached over SSH, and it has nowhere durable to write a result.

Control

k-anonymity gate

Reject groups under 10

What it does
Stops a result that would re-identify someone. Individual rows are refused. Any group with fewer than 10 members is refused. Only a coarse aggregate may continue.
Technology
An automated k-anonymity check, with differential privacy on released aggregates. Threshold in this blueprint: N < 10 is rejected.
Mandate
Controlled aggregation. A benchmark cannot be a single company with the name removed.

Control

KMS signing

Key never leaves the HSM

What it does
Signs one canonical JSON document: the query hash, the caller, the time, the S3 object versions, and the released row count. KMS signs the SHA-256 digest of that document. The private key is not loaded into the enclave.
Technology
AWS KMS asymmetric digital signature. kms:Sign with RSASSA_PSS_SHA_256 and MessageType DIGEST. The private key stays in the HSM. This key does not rotate automatically. The symmetric key that encrypts S3 objects is a different key, and that one does.
Mandate
Cryptographic attestation. Later, an IC memo can point at a signature instead of an unlocked file.

Egress

Analyst viewport

Pixels only, watermarked

What it does
This is the viewing layer, not the enclave. The associate sees the accepted aggregate as pixels under a diagonal watermark. Clipboard, download, and screen capture are off. A photograph or OCR can still copy the glass; the watermark is what makes that copy traceable.
Technology
AppStream 2.0 or WorkSpaces as a VDI. The pixel stream is separate from Nitro Enclave compute. The watermark is a rotated brick of the viewer’s identity, plus an opaque footer so a crop still carries the name.
Mandate
Zero-egress viewport. The only thing that crosses the boundary is pixels, and those pixels name the person watching.

Mandates

Four rules the design does not get to bend.

  1. 01

    No raw records on a laptop

    Financial and health records are not written to an analyst’s endpoint. The blueprint processes them in hardware-encrypted, ephemeral memory inside an enclave or a remote desktop.

  2. 02

    Cell-level access control

    People query through a governance catalog. LP names, account numbers, and medical identifiers are masked at read time from the caller’s role. Rows are limited to the fund, book, or region that role may see.

  3. 03

    Keys stay in the HSM

    Signing keys are not exposed in application memory. The blueprint signs inside a hardware security module, using short-lived credentials from a hardware-attested certificate.

  4. 04

    Nothing leaves but pixels

    Analysts see a read-only, watermarked pixel stream. Clipboard, local download, and screen capture are disabled. There is no file to forward and nothing for the clipboard to hold.

For a GP

What each control is for on a diligence desk.

The audience is a general partner and the people who prepare an investment committee. The data is the firm’s own, the portfolio’s metrics, and proprietary industry files used to diligence a company.

  • Cell-level masking

    Masked LP and portfolio-company identifiers

    A principal reviewing a sector cut sees PORTCO-•••7 and LP-•••2. Legal names stay masked unless that person’s role is cleared for the cell.

  • k-anonymity gate

    k-anonymity so benchmarks cannot re-identify a single company

    A cohort average is released only when the group has at least 10 companies. A three-company slice is rejected, so a benchmark cannot be walked back to one portco.

  • Zero-egress viewport

    Watermarked view-only data rooms

    An associate reads a diligence file as pixels, with their name on the watermark. The file is not downloaded, and the clipboard stays empty.

  • Signed audit trail

    A signed audit trail for IC memos

    The figure in an investment-committee memo points at a signed query: who ran it, when, which object version it read, and how many rows came back.

Functional spec

FS-1 through FS-4.

Requirements of the blueprint, so a diligence team can see what the system would have to do. This is not a feature list of the current product.

FS-1Governed cell-level read access

Analysts write ANSI SQL. Every statement passes through a centralized governance catalog before it reads. Masking is applied at read time: an unprivileged analyst sees a redacted account number (XXXX-XXXX-1234) and a token instead of an LP or company legal name. Row-level filters restrict results to that person’s fund, region, or book.

FS-2Ephemeral enclave processing, with no spill

The query runs in a Nitro Enclave. The Nitro Hypervisor gives that enclave its own memory, which the parent instance cannot read, including as root. That isolation is not AMD SEV-SNP and not Intel SGX. Attestation is the enclave document (PCR measurements), not NitroTPM. NitroTPM is for measured boot of the parent. A confidential VM, if the design uses one instead of a Nitro Enclave, would be AMD SEV-SNP or Intel TDX. Intel SGX is deprecated on many current parts and is not a target. AppStream or WorkSpaces is the viewing desktop, not this compute. There is no swap and no core dump.

FS-3Controlled aggregation exports

Anything that would leave the enclave has to pass a release gate. k-anonymity and differential privacy are different controls, and both belong here. Individual rows are refused, and any group with N < 10 is refused. A single query gate is not enough: overlapping queries can be subtracted to recover a company the gate would have rejected on its own. The blueprint adds a query auditor for overlapping cohorts, a per-analyst and per-dataset privacy budget, noise on released aggregates, and rate limits. Example epsilon values on this page are illustrative, not a setting for a fund.

FS-4Audit lineage and non-repudiation

Every SQL statement is stored with the caller’s identity, the enclave attestation measurements, the row count, and the S3 object versions that were scanned. The log is append-only: CloudTrail and CloudWatch Logs, plus S3 Object Lock in Compliance mode for 7 years. A later reader can show the record was not edited. The signature over the canonical payload is stored with that record.

Technical controls

Layer, technology, configuration.

The stack the blueprint would use. Naming a control is not a claim that TrueNorth has been assessed against it.

  • Data lake storage

    AWS S3 (Express or Standard)

    Symmetric customer-managed key with automatic rotation, held in a FIPS 140-3 Level 3 HSM. This is the data key, not the asymmetric signing key. Bucket policy allows TLS 1.3 only. Analytical roles are explicitly denied writes and deletes.

  • Governance catalog

    Lake Formation or Apache Ranger

    Cell-level and row-level rules, plus dynamic masking. A role receives the minimum cells required for its question.

  • Isolated compute

    Nitro Enclaves

    Ephemeral compute. No persistent storage and no SSH. Isolation is the Nitro Hypervisor, not SEV or SGX. Attestation is a Nitro Enclaves document (PCRs). NitroTPM measures the parent instance. A confidential VM alternative is AMD SEV-SNP or Intel TDX. Intel SGX is not a target.

  • Viewing layer

    AppStream 2.0 or WorkSpaces

    VDI only. Pixels, a diagonal watermark, and an opaque footer. Clipboard, download, and print are off. This desktop is not the enclave.

  • Identity and access

    IAM Roles Anywhere or OIDC

    Short-lived credentials, one hour at most, issued over mutual TLS with a hardware-backed X.509 client certificate. No long-lived API keys.

  • Key management

    AWS KMS asymmetric signing

    An asymmetric digital signature, RSASSA_PSS_SHA_256 via kms:Sign with MessageType DIGEST, is computed inside the KMS HSM. This key does not rotate automatically. The private key never enters application memory. Auditors check it with kms:Verify, or offline after one kms:GetPublicKey.

  • Audit and retention

    CloudTrail and S3 Object Lock

    CloudTrail and CloudWatch Logs, plus S3 Object Lock in Compliance mode for 7 years. The blueprint aligns with that recordkeeping control. It is not a certification.

Signing pipeline

A signature that never sees the private key.

The source note stopped after the result set, and its signing line did not match the verifier. This blueprint uses one payload in both places. Steps 5 through 10 carry the sequence through the viewport, an auditor check, and the SIEM triggers. Those steps are marked as part of this blueprint.

The first source note described a keyed hash over a concatenation of four fields. This blueprint does not use that construction. The operation is an asymmetric digital signature, RSASSA_PSS_SHA_256, via kms:Sign. The signed bytes are one canonical JSON document: sorted keys, compact separators, and s3_object_versions as objects sorted by bucket, key, and version id. KMS signs the SHA-256 digest of that document (MessageType DIGEST) so a long version list is not cut off by the 4,096-byte limit on a raw message. An offline check still passes the payload bytes to the cryptography library, which hashes them the same way.

Signing formula

Scroll sideways inside the formula. The page stays still.

payload = CanonicalJSON({query_hash, user_arn, timestamp, s3_object_versions, row_count})
# sorted keys, separators ',' and ':'
# s3_object_versions: {bucket, key, version_id}, sorted by those fields
Digest = SHA-256(payload)
Signature = kms:Sign(KeyId, Message=Digest, MessageType=DIGEST, SigningAlgorithm=RSASSA_PSS_SHA_256)
  1. 1

    Analyst → catalog

    Analyst query

    The analyst submits plaintext ANSI SQL from outside the trust boundary.

  2. 2

    Catalog enforces the read

    Catalog masking

    Lake Formation or Ranger applies cell masking and row filters before any value is returned.

  3. 3

    Catalog and S3 → enclave

    Enclave execution

    DuckDB or Trino runs the query inside the enclave, in volatile RAM. The S3 read is an encrypted stream.

  4. 4

    Engine → enclave memory

    Result set

    The result exists only in enclave memory. It is not written as a file.

  5. 5

    Result set → gate

    k-anonymity and differential privacy

    The gate rejects individual rows and any group with N < 10. Only an accepted aggregate continues.

    Completed in this blueprint

  6. 6

    Gate → KMS HSM

    Canonical payload and KMS sign

    The enclave builds one JSON document — query hash, caller, timestamp, S3 object versions, and released row count — with sorted keys and compact separators. Each object version is bucket, key, and version id. KMS signs the SHA-256 digest of that document with RSASSA_PSS_SHA_256. The private key stays in the HSM.

    Completed in this blueprint

  7. 7

    Signature → locked log

    Audit write

    The signed record is appended to CloudTrail and to an S3 object that Object Lock will not allow to be overwritten or deleted.

    Completed in this blueprint

  8. 8

    Enclave → pixels

    Watermarked viewport

    The accepted aggregate is rendered to the analyst’s view-only stream. The watermark text is rotated and tiled diagonally, and it names the viewer. No file and no clipboard payload cross the boundary. A photograph or OCR can still copy the pixels; the watermark is the trace.

    Completed in this blueprint

  9. 9

    Locked log → auditor

    Auditor verification

    An auditor checks the same canonical JSON with kms:Verify, or offline. Offline, they fetch the public key once with kms:GetPublicKey and verify RSASSA-PSS (MGF1-SHA256, salt length equal to the digest length) in the cryptography library. After that fetch they do not need a live KMS session, the private key, or enclave memory.

    Completed in this blueprint

  10. 10

    Gate, host, and KMS → SIEM

    SIEM triggers

    A rejected group under 10 members raises a medium alert and is not released. A memory dump or ptrace attempt terminates the enclave and revokes the session. A non-VPC S3 read is a severity-1 escalation. A failed signature check quarantines the exported artifact.

    Completed in this blueprint

Reference implementation

Samples a reviewer can read, not a system we run.

The blocks below are illustrative. Account 123456789012 and every ARN are placeholders. Copying them does not configure a live account, and TrueNorth does not execute them.

4.2IAM read-only policy

Analytical roles can read one prefix, only through the example VPC endpoint, only over TLS. They can decrypt only when S3 calls KMS. Write, delete, and ACL changes are explicit denies. Account 123456789012, the bucket name, the key id, and vpce-0a1b2c3d4e5f67890 are placeholders.

Analytical execution rolejsonIllustrative sample

Scroll sideways inside the sample. The page stays still.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowS3ReadOnlySecureData",
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:GetObjectVersion"],
      "Resource": "arn:aws:s3:::zde-secure-data-bucket/analytics/*",
      "Condition": {
        "StringEquals": { "aws:SourceVpce": "vpce-0a1b2c3d4e5f67890" },
        "Bool": { "aws:SecureTransport": "true" }
      }
    },
    {
      "Sid": "AllowListDataPrefixOnly",
      "Effect": "Allow",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::zde-secure-data-bucket",
      "Condition": {
        "StringLike": { "s3:prefix": ["analytics/", "analytics/*"] },
        "StringEquals": { "aws:SourceVpce": "vpce-0a1b2c3d4e5f67890" },
        "Bool": { "aws:SecureTransport": "true" }
      }
    },
    {
      "Sid": "AllowDecryptViaS3Only",
      "Effect": "Allow",
      "Action": "kms:Decrypt",
      "Resource": "arn:aws:kms:us-east-1:123456789012:key/11111111-2222-3333-4444-555555555555",
      "Condition": {
        "StringEquals": { "kms:ViaService": "s3.us-east-1.amazonaws.com" }
      }
    },
    {
      "Sid": "DenyAllWriteDeleteAndAclChanges",
      "Effect": "Deny",
      "Action": ["s3:PutObject", "s3:DeleteObject", "s3:DeleteObjectVersion", "s3:PutObjectAcl"],
      "Resource": [
        "arn:aws:s3:::zde-secure-data-bucket",
        "arn:aws:s3:::zde-secure-data-bucket/*"
      ]
    },
    {
      "Sid": "DenyInsecureTransport",
      "Effect": "Deny",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::zde-secure-data-bucket",
        "arn:aws:s3:::zde-secure-data-bucket/*"
      ],
      "Condition": { "Bool": { "aws:SecureTransport": "false" } }
    }
  ]
}
5BAuditor verification

A live kms:Verify call sends the SHA-256 digest with MessageType DIGEST. An auditor who should stay independent of that account fetches the public key once with kms:GetPublicKey and verifies later, offline, with the cryptography library, passing the payload bytes. Padding is PSS with MGF1-SHA256. The salt length is the SHA-256 digest length, 32 bytes, which is what AWS KMS uses for RSASSA_PSS_SHA_256.

Verify, live or offlinepythonIllustrative sample

Scroll sideways inside the sample. The page stays still.

"""Illustrative sample. Account 123456789012 and every ARN are placeholders.
Nothing here calls a live key.

query_hash e3b0c442…b855 is SHA-256 of the empty string. It is a placeholder,
not a hash of a real query. hsm_signature is a truncated stand-in, not a
signature KMS produced. An RSA-2048 signature is 256 bytes.
"""

import json


def canonical_payload(provenance: dict) -> bytes:
    """One document, sorted keys, compact separators (',' and ':').

    s3_object_versions is a list of {bucket, key, version_id} objects,
    sorted by those three fields. row_count is an integer.
    """
    versions = sorted(
        (
            {"bucket": v["bucket"], "key": v["key"], "version_id": v["version_id"]}
            for v in provenance["s3_object_versions"]
        ),
        key=lambda v: (v["bucket"], v["key"], v["version_id"]),
    )
    document = {
        "query_hash": provenance["query_hash"],
        "user_arn": provenance["user_arn"],
        "timestamp": provenance["timestamp"],
        "s3_object_versions": versions,
        "row_count": int(provenance["row_count"]),
    }
    return json.dumps(document, sort_keys=True, separators=(",", ":")).encode("utf-8")


def verify_enclave_report_signature(provenance: dict, kms_key_arn: str, region: str) -> bool:
    """Live check. Needs kms:Verify. MessageType=DIGEST stays under the 4,096-byte RAW limit.

    Offline verification passes the payload bytes to the cryptography library,
    which hashes them. A DIGEST signature matches that check.
    """
    import hashlib
    import boto3

    signature = bytes.fromhex(provenance["hsm_signature"])
    digest = hashlib.sha256(canonical_payload(provenance)).digest()
    kms = boto3.client("kms", region_name=region)
    response = kms.verify(
        KeyId=kms_key_arn,
        Message=digest,
        MessageType="DIGEST",
        Signature=signature,
        SigningAlgorithm="RSASSA_PSS_SHA_256",
    )
    if response["SignatureValid"]:
        print("[VERIFIED] asymmetric signature matches the canonical payload")
        return True
    print("[CRITICAL] SIGNATURE INVALID")
    return False


def fetch_public_key_once(kms_key_arn: str, region: str) -> bytes:
    """One kms:GetPublicKey. Store the DER SubjectPublicKeyInfo with the audit file.

    Later checks do not need a live KMS session.
    """
    import boto3

    kms = boto3.client("kms", region_name=region)
    return kms.get_public_key(KeyId=kms_key_arn)["PublicKey"]


def verify_offline(provenance: dict, public_key_der: bytes) -> bool:
    """Offline check. PSS, MGF1-SHA256, salt length = SHA-256 digest length (32).

    That salt length is what AWS KMS uses for RSASSA_PSS_SHA_256.
    Do not pass PSS.AUTO or MAX_LENGTH.
    """
    from cryptography.hazmat.primitives import hashes
    from cryptography.hazmat.primitives.asymmetric import padding
    from cryptography.hazmat.primitives.serialization import load_der_public_key

    public_key = load_der_public_key(public_key_der)
    public_key.verify(
        bytes.fromhex(provenance["hsm_signature"]),
        canonical_payload(provenance),
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=hashes.SHA256.digest_size,
        ),
        hashes.SHA256(),
    )
    print("[VERIFIED] offline, with the public key only")
    return True


SAMPLE_PROVENANCE = {
    # SHA-256("") — placeholder only, not a hash of a query.
    "query_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "user_arn": "arn:aws:iam::123456789012:role/DefenseAnalystRole",
    "timestamp": "2026-10-09T17:15:00Z",
    "s3_object_versions": [
        {
            "bucket": "zde-secure-data-bucket",
            "key": "analytics/claims/2026/09/part-0000.parquet",
            "version_id": "3HL4kqtJlcpXroDTDmJ.rmSpXd3dIbrHY",
        },
        {
            "bucket": "zde-secure-data-bucket",
            "key": "analytics/claims/2026/09/part-0001.parquet",
            "version_id": "Ho6vPy1uHrLr2Q0fJ0sQ5FRz9p3eQZ1a",
        },
    ],
    "row_count": 142,
    # Truncated stand-in from the source note ("3a8f9c..."). Not a real signature.
    "hsm_signature": "3a8f9c",
}

EXAMPLE_KEY = "arn:aws:kms:us-east-1:123456789012:key/00000000-0000-0000-0000-000000000000"
5CWatermarked viewport

The overlay is the watermark text only, staggered in a brick pattern and rotated so the label tiles diagonally. An opaque footer repeats the same label so a crop of the middle still carries it. The data lines stay upright. Pixel rendering deters casual copying. It does not stop someone photographing the screen or running OCR on a picture of it. The watermark adds traceability: a copy still names who was allowed to see the frame.

Diagonal watermarkpythonIllustrative sample

Scroll sideways inside the sample. The page stays still.

"""Illustrative sample. The canvas lives in RAM. This function does not write a file.

Pixel rendering deters casual copying. It does not stop a photograph of the
screen or OCR. The diagonal watermark is there so a copy can still be traced
to the role and the time.
"""

import cv2
import numpy as np
import pandas as pd


def render_watermarked_viewport(frame: pd.DataFrame, user_arn: str, timestamp: str) -> np.ndarray:
    lines = frame.to_string(index=False).splitlines()
    width = 1200
    line_height = 25
    height = line_height * (len(lines) + 2)
    canvas = np.full((height, width, 3), 255, dtype=np.uint8)

    y = line_height
    for line in lines:
        cv2.putText(canvas, line, (16, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (20, 20, 20), 1, cv2.LINE_AA)
        y += line_height

    # Watermark text only. Rotated, then blended, so the label tiles diagonally.
    overlay = np.zeros_like(canvas)
    label = f"CONFIDENTIAL - {user_arn} - {timestamp}"
    # Brick stagger, then rotate, so the label tiles diagonally at about 15% opacity.
    for row_i, row in enumerate(range(-height, height * 2, 180)):
        offset = 210 if row_i % 2 else 0
        for col in range(-width, width * 2, 420):
            cv2.putText(
                overlay, label, (col + offset, row), cv2.FONT_HERSHEY_SIMPLEX, 0.55, (160, 40, 40), 1, cv2.LINE_AA
            )
    center = (width // 2, height // 2)
    matrix = cv2.getRotationMatrix2D(center, -24, 1.0)
    rotated = cv2.warpAffine(overlay, matrix, (width, height))
    frame_px = cv2.addWeighted(canvas, 0.85, rotated, 0.15, 0)
    # Opaque footer so the label survives a crop of the tiled area.
    cv2.rectangle(frame_px, (0, height - line_height), (width, height), (235, 235, 235), -1)
    cv2.putText(frame_px, label, (16, height - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (20, 20, 120), 1, cv2.LINE_AA)
    return frame_px
6IaC hardening

Asymmetric KMS keys do not rotate automatically. enable_key_rotation is false on purpose. Rotate by creating a new key, moving the alias, and keeping the old public key verifiable for the retention period. The symmetric SSE-KMS key on the data bucket is a different key, and that one should use automatic rotation. The signing-key policy keeps an account-root administration statement and a KeyAdmin role with no kms:Sign or kms:Verify, plus a deny so only the enclave role can sign.

KMS key and policyhclIllustrative sample

Scroll sideways inside the sample. The page stays still.

# Illustrative sample. Account 123456789012 is a placeholder.
# Asymmetric KMS keys do not support automatic rotation.
# enable_key_rotation = false makes that explicit. Omit the argument, or set
# false. Do not set true: the API rejects it for SIGN_VERIFY keys.
# A policy with no admin statement can lock the account out of key management.

resource "aws_kms_key" "defense_signing_key" {
  description              = "Example provenance signing key. Not a live key."
  customer_master_key_spec = "RSA_2048"
  key_usage                = "SIGN_VERIFY"
  deletion_window_in_days  = 30
  enable_key_rotation      = false

  policy = data.aws_iam_policy_document.defense_signing_key.json
}

data "aws_iam_policy_document" "defense_signing_key" {
  # Account root can administer the key through IAM. Without this, a bad
  # policy can make the key unmanageable.
  statement {
    sid       = "EnableRootAccountAdministration"
    effect    = "Allow"
    actions   = ["kms:*"]
    resources = ["*"]
    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::123456789012:root"]
    }
  }

  # KeyAdmin manages the key. It cannot sign or verify.
  statement {
    sid    = "AllowKeyAdministrationNoCryptoUse"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::123456789012:role/KeyAdmin"]
    }
    actions = [
      "kms:DescribeKey",
      "kms:GetKeyPolicy",
      "kms:PutKeyPolicy",
      "kms:EnableKey",
      "kms:DisableKey",
      "kms:ScheduleKeyDeletion",
      "kms:CancelKeyDeletion",
      "kms:TagResource",
      "kms:UntagResource",
      "kms:GetPublicKey",
    ]
    resources = ["*"]
  }

  # Sign is the only cryptographic operation the enclave role receives.
  # DIGEST avoids the 4,096-byte limit on RAW messages.
  statement {
    sid    = "AllowEnclaveSigningOnly"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::123456789012:role/EnclaveExecutionRole"]
    }
    actions   = ["kms:Sign"]
    resources = ["*"]
    condition {
      test     = "StringEquals"
      variable = "kms:SigningAlgorithm"
      values   = ["RSASSA_PSS_SHA_256"]
    }
    condition {
      test     = "StringEquals"
      variable = "kms:MessageType"
      values   = ["DIGEST"]
    }
  }

  statement {
    sid    = "AllowPublicVerification"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = ["arn:aws:iam::123456789012:role/AuditorRole"]
    }
    actions = [
      "kms:Verify",
      "kms:GetPublicKey",
      "kms:DescribeKey",
    ]
    resources = ["*"]
  }

  # Even if an IAM policy grants kms:Sign, only the enclave role may use it.
  statement {
    sid       = "DenySignExceptEnclaveRole"
    effect    = "Deny"
    actions   = ["kms:Sign"]
    resources = ["*"]
    principals {
      type        = "AWS"
      identifiers = ["*"]
    }
    condition {
      test     = "ArnNotEquals"
      variable = "aws:PrincipalArn"
      values   = ["arn:aws:iam::123456789012:role/EnclaveExecutionRole"]
    }
  }
}
sysctlconfIllustrative sample

Scroll sideways inside the sample. The page stays still.

# Illustrative host hardening for the enclave image in this blueprint.
# FS-2 requires no swap. vm.swappiness = 0 does not detach a swap device
# that is already on. Also run: swapoff -a

fs.suid_dumpable = 0

# Discard core dumps. The value is a pipe to false, not a file path.
kernel.core_pattern = |/bin/false

# 3 disables ptrace for every user, including root.
# It cannot be lowered again until reboot.
# 0 is classic ptrace, 1 is restricted, 2 is admin-only, 3 is no ptrace.
kernel.yama.ptrace_scope = 3

# Restricts auto-loading of TTY line-discipline modules.
# Unprivileged callers can no longer trigger those modules.
dev.tty.ldisc_autoload = 0

fs.protected_fifos = 2
fs.protected_regular = 2
kernel.randomize_va_space = 2

# Hide kernel pointers from every user, including root.
kernel.kptr_restrict = 2
# Restrict dmesg to privileged readers.
kernel.dmesg_restrict = 1
# Deny unprivileged perf_event_open.
# Mainline kernels treat 2 as their ceiling. Some distribution kernels honor 3.
# Check the kernel you actually boot.
kernel.perf_event_paranoid = 3

vm.swappiness = 0
No swapbashIllustrative sample

Scroll sideways inside the sample. The page stays still.

# FS-2: no swap device. swappiness 0 does not remove one that is already active.
swapoff -a
# Comment swap lines out of fstab so they do not return on boot.
# Then mask the unit that would turn swap back on.
# systemctl mask swap.target
7SIEM triggers

Four conditions the blueprint would hand to a SIEM. In the query walkthrough, only the k-anonymity rejection fires.

  • K-anonymity rejection (N < 10)

    Medium alert. Log the IAM context. The group is not released.

  • Memory dump attempt or ptrace call

    Terminate the enclave and revoke the IAM session.

  • Non-VPC S3 access attempt

    Severity 1 escalation to the SOC.

  • KMS verification failure

    Quarantine the exported report artifact.

Sign-off

The design stacks controls across storage, transport, execution, and export: no unencrypted local storage of raw records, hardware-attested enclaves in the blueprint, and an asymmetric signature made inside the HSM. It is designed to align with the control objectives of IL5, IL6, HIPAA, and FINRA. It is designed to satisfy those control objectives on paper. It does not meet those frameworks, and it is not a certification or an attestation. Out of scope: a DoD authorization for IL5 or IL6, the HIPAA risk analysis, and the SEC 17a-4(f) third-party undertaking. TrueNorth does not run this system.

This page is a reference architecture: how we would design zero-egress analytics, and the standard we build toward. TrueNorth does not run this system. There are no Nitro enclaves in the product, and TrueNorth is not certified for IL5, IL6, HIPAA, or FINRA. Names of those frameworks mean the design is written to align with the controls these frameworks require.

Glossary

Terms the rest of the page uses.

CLAC
Cell-level access control. Masking and permissions on individual columns, based on the caller’s role.
RLAC
Row-level access control. Filters that limit which rows a person can see, such as by fund or region.
k-anonymity
A released group is supposed to be indistinguishable from at least k − 1 others. Here, a group with fewer than 10 members is suppressed.
Differential privacy and ε
Noise added to an aggregate so one person’s presence is hard to pick out. ε is the privacy budget. Smaller ε is stricter and noisier. Figures on this page are illustrative.
Enclave
Isolated compute for the query. In this blueprint that is a Nitro Enclave, separated by the Nitro Hypervisor, or a confidential VM using AMD SEV-SNP or Intel TDX.
Attestation
A signed measurement of what code ran. For Nitro Enclaves that is an attestation document with PCR values. NitroTPM attests the parent instance, not the enclave.
HSM
Hardware security module. The signing key is used inside AWS KMS and is not exported into the application.
WORM
Write once, read many. Here, S3 Object Lock in Compliance mode for 7 years, so the audit record cannot be overwritten.
VDI
Virtual desktop. AppStream 2.0 or WorkSpaces. This is where a person looks at pixels. It is not the enclave.
SIEM
The system that receives security events. In this blueprint that is Security Hub plus the log pipeline, with a response for each trigger.

NIST SP 800-53

A map of control families, not an authorization.

An indicative map to NIST SP 800-53 Rev. 5 identifiers. It is not a control-implementation statement and not an authorization package.

  • AC

    AC-3Access enforcement

    Catalog cell and row rules, and the read-only role.

  • AC

    AC-4Information flow enforcement

    VPC endpoints, the egress firewall, and the pixel viewport.

  • AC

    AC-6Least privilege

    Separate key admin, signer, and auditor. Analysts cannot write.

  • AC

    AC-17Remote access

    Analysts enter through the managed virtual desktop.

  • AU

    AU-2 / AU-3Audit events and content

    SQL, identity, attestation, row count, and object versions.

  • AU

    AU-6Audit review

    SIEM triggers and the response playbook.

  • AU

    AU-9Protection of audit information

    CloudTrail and Object Lock in Compliance mode.

  • AU

    AU-10Non-repudiation

    The asymmetric signature over the canonical payload.

  • AU

    AU-11Audit retention

    Seven-year WORM retention.

  • SC

    SC-7Boundary protection

    Private subnets, no internet gateway, firewall default-deny.

  • SC

    SC-8Transmission confidentiality

    TLS to S3 and KMS. Insecure transport is denied.

  • SC

    SC-12Key management

    Separate data key and signing key. The signing key does not auto-rotate.

  • SC

    SC-28Protection at rest

    SSE-KMS on the data and audit buckets.

  • SI

    SI-4System monitoring

    The SIEM triggers, from the release gate through blocked DNS.

  • SI

    SI-7Integrity

    Enclave attestation and signature verification.

  • SI

    SI-19De-identification

    Masking, k-anonymity, and the privacy budget.

  • IA

    IA-5Authenticator management

    Credentials last at most an hour. No long-lived access keys.

Download the full spec (PDF)

What this means for your fund

The workbook stays inside the line.

  • Ask a portfolio question without copying the workbook onto a laptop.
  • Publish a benchmark that cannot be reversed into one company.
  • Keep a diligence room as a watermarked picture, tied to the person who viewed it.
  • Cite an IC memo from a signed query instead of an unlocked spreadsheet.

If this is the bar you want for portfolio data, LP records, or a diligence room, we should talk. The page you just read is the design. It is not a system TrueNorth operates today.