Security whitepaper

Version 1.1 · 10 August 2026 · CC-BY 4.0

Version 1.1 · 2026-08-10 · CC-BY 4.0


Abstract

Nduzem is a file-transfer service where the operator provably cannot read the content it stores or forwards. Files are encrypted on the sender's device with a per-transfer key sealed to the recipient's long-lived identity key (app mode) or shared out-of-band via a URL fragment (link mode). The server sees ciphertext, a byte-count, and a hash; it never learns filename, MIME type, plaintext length, or a way to construct the decryption key. The client verifies content integrity via a rolling SHA-256 and sender authenticity via an Ed25519 signature over that hash, both bound to the recipient's declared trust in the sender's public keys. This paper describes the protocol, its threat model, and its explicit non-goals. It is written for readers who want to verify the cryptographic claims rather than take them on trust; client-side design decisions cite the corresponding architectural-decision records (ADRs), which are published alongside the client source.

1. Introduction and motivation

The word "encrypted" has been stretched thin in the file-transfer market. When WeTransfer or Dropbox advertise encryption, they mean their servers use disk-at-rest encryption and TLS on the wire, however a provider running SELECT against a customer database can still read every file. In regulated contexts, such as legal discovery, medical records, journalism, incident response, that guarantee is often insufficient. What is needed is end-to-end: the file is unreadable to any party the sender did not intend to reach, including the operator, including under compelled disclosure.

End-to-end encryption is well understood for real-time messaging, where Signal's Double Ratchet [1] has become the reference design. It maps awkwardly to file transfer for three reasons. First, files are one-shot rather than continuous; precisely, forward secrecy per-message is not naturally per-file. Second, recipients frequently have no existing account with the sender's provider. This makes link-mode delivery a first-class use case, not an edge case. Third, files are larger than messages by orders of magnitude. Consequently, streaming, integrity, and resumability need to work on multi-gigabyte payloads on consumer-grade devices.

Firefox Send addressed some of these (link-mode) with in-browser decryption via URL fragments and closed in 2020 after abuse-report handling proved too costly to sustain [2]. The lesson is not that end-to-end file transfer is unworkable; it is that abuse handling and content integrity are load-bearing pieces of the design that must be built in, not bolted on.

Nduzem is a zero-knowledge file-transfer protocol with app-mode and link-mode delivery, streaming send and receive, per-transfer ciphertext hashing, sender-signature verification, and a cooperative abuse-report mechanism that operates over ciphertext hashes without compromising the encryption of unrelated transfers. The design principle it optimises for is minimum server knowledge: every decision is measured against what it forces the server to learn.

This paper covers the v1 protocol. §2 states the threat model. §3 describes the cryptographic architecture. §4 walks the send and receive flows. §5 covers the trust model. §6 enumerates what the server can and cannot see. §7 describes the data lifecycle. §8 covers the additional protections. §9 compares the design to related work. §10 lists explicit non-goals and the v2 roadmap. §11 collects references.

2. Threat model

The adversaries below are listed in decreasing order of resources we expect the design to defeat.

2.1 Honest-but-curious server operator

Someone with legitimate root access to the server host, a sysadmin, a database administrator, a stack operator, who wants to read stored files or reconstruct who-sent-what-to-whom without overtly breaking rules. This is the primary adversary the protocol is designed against. We defeat this adversary by ensuring the server never possesses the material required to decrypt.

2.2 Compromised server or compelled disclosure

An attacker who has either taken control of the server host or compelled the operator to hand over everything the server has lawful access to. This is the same adversary as §2.1 with the gloves off. Our defence is the same: absence of decryption material in the server. What such an attacker gains is metadata includes sender and recipient account identifiers, byte counts, timestamps, and ciphertext-object references. They do not gain the ability to decrypt in-flight or archived transfers.

Where a user has opted in to key backup (§3.6), this adversary also obtains that user's sealed key container. It is a crypto_secretbox under a key derived from a recovery key that has never been transmitted to the server and cannot be derived from anything the server stores. A compelled-disclosure order can therefore compel the container and still not produce a readable key. We state this explicitly because "the operator stores an encrypted copy of your private key" is a claim that deserves to be examined rather than waved past.

2.3 Passive network adversary

An attacker who can observe TLS-encrypted traffic to and from the server but cannot decrypt it. This adversary sees connection timing, packet sizes, and hostnames via SNI. We do not defend against traffic-analysis attacks in v1, however size padding was considered and deferred. The threat model acknowledges this: an attacker who logs enough sender ⇄ recipient pairs and file sizes can infer relationships that the plaintext would confirm.

2.4 Active network adversary and TLS MITM

An attacker who can intercept and alter TLS-encrypted traffic. In practice this requires either a rogue certificate authority in the user's trust store or a compromised endpoint. Our defence layers on top of standard TLS: certificate pinning at the client is planned (v2 roadmap) but not shipped in v1. The client does verify the sender's identity public key via an out-of-band fingerprint mechanism (§5); an active MITM cannot forge a valid sender signature over a substituted blob_sha256 without also holding the sender's Ed25519 signing key, which the server never holds in usable form (§3.6) and a network adversary never sees at all.

2.5 Malicious sender or malicious recipient

A user who is themselves inside the cryptographic envelope. This is the weakest adversary and the one the design explicitly does not try to defeat: a recipient who receives a file can obviously read it, screenshot it, and share it further. A sender can send whatever they choose to send. What the protocol does provide, even here, is non-repudiation: the sender's Ed25519 signature over the ciphertext hash proves to the recipient which key material was used, and via the sender-public-key mechanism (§5) the recipient can bind that to a durable identity.

2.6 Endpoint compromise

Explicitly out of scope. If the sender's or recipient's device is compromised (hostile OS, keylogger with root, malicious browser extension) the file is compromised. The design does not attempt to defend the plaintext once it has left the sodium primitive on either end. This is documented as a limitation, not a defence-in-depth opportunity to be hardened around: attempting to would inflate complexity without meaningfully changing the adversary's economics.

Key backup (§3.6) extends this surface off the device. A recovery key is designed to be written down, which means it can also be photographed, synced to a cloud notes app, or stored in a password manager that is later breached — none of which involve compromising the device at all. Anyone holding both the recovery key and the account credentials can reconstruct the identity key and read everything sealed to it.

We consider this an acceptable trade because it is opt-in and because the alternative it displaces is worse: users who are told that key loss is unrecoverable improvise their own backups, and an improvised backup of a private key is rarely a crypto_secretbox. The mitigation is honesty at the point of generation — the client tells the user, when it shows the recovery key, that anyone holding it can read their files.

2.7 Store-now-decrypt-later (quantum)

A resourced attacker who records ciphertext today with the intent of decrypting once large-scale fault-tolerant quantum computers become available. Curve25519 and Ed25519 are both broken by a sufficiently capable quantum adversary via Shor's algorithm. This risk is acknowledged: the v2 roadmap discusses the migration path to a hybrid classical-plus-post-quantum construction. v1 ships without post-quantum protection because the primitives available in stable libsodium do not yet include a settled Kyber/Dilithium implementation, and shipping a home-grown one would be worse than the status quo.

3. Cryptographic architecture

The protocol composes three layers of key material: (1) long-lived per-user identities; (2) per-transfer symmetric keys, and (3) per-chunk AEAD tags. Each of these addresses a distinct property.

3.1 Per-user long-lived keys

Every registered user has two keypairs generated on their device at first registration:

Storage on the device is platform-native: Keychain on iOS, Keystore-backed Encrypted SharedPreferences on Android, IndexedDB wrapped by an origin-scoped AES key held in SubtleCrypto on web. Each installed device slot is per-user: a device with multiple accounts holds separate keypairs under separate slots (client ADR-0011) rather than a single overwritable slot.

Key rotation is user-initiated, never automatic. A user may replace their identity keypair at any time, gated on password re-authentication plus a second factor where one is enrolled; the consequence — every transfer sealed to the old key becomes permanently unreadable — is acknowledged explicitly before the rotation proceeds.

A device that loses its identity keypair loses the ability to decrypt any transfer sealed to it. Unless the user has opted in to a key backup (§3.6), that loss is permanent by construction: the server has never held anything that could reconstruct the key. The client warns loudly on the verify-contact flow when a contact's public key differs from what was previously verified out-of-band (§5).

3.2 Per-transfer symmetric keys

Every transfer generates a fresh 32-byte key, K_file, via libsodium's crypto_secretstream_keygen. This key is used exactly once, for one transfer, and is discarded on the sender's device after the send completes.

In app mode, K_file is sealed to the recipient's identity public key via crypto_box_seal and travels through the server as the wrapped_key field on the transfer envelope. The server sees the sealed bytes; only the recipient's identity private key can unseal them.

In link mode, K_file is base64url-encoded and appended to the share URL as a fragment: https://nduzem.com/r/<id>#<K_file>. Browsers do not send URL fragments in HTTP requests, so the server never sees K_file for link-mode transfers. The recipient's browser extracts the fragment client-side and passes it into the decrypt routine locally.

Neither mode grants the server the material to decrypt.

3.3 Content encryption - the OS4S container

File bytes are encrypted with libsodium's crypto_secretstream_xchacha20poly1305 [3], which provides a streaming AEAD with a randomly chosen per-encryption 24-byte header and 17-byte AEAD tags on each 64 KiB chunk. The wire format is:

[4-byte magic prefix: 'O','S','4','S']
[24-byte secretstream header]
[chunk_1: up to 64 KiB plaintext + 17-byte AEAD tag]
[chunk_2: ...]
...
[chunk_N: last chunk, tag = FINAL]

Every chunk is authenticated with a Poly1305 tag over the ciphertext and its position in the stream, so truncation, reordering, and byte-level tampering are all detected. The FINAL tag on the last chunk defends against truncation attacks that would otherwise be undetectable at the AEAD layer.

The 64 KiB plaintext-chunk size (client ADR-0003) is chosen for two reasons. It is small enough that memory usage during encryption and decryption stays proportional to one chunk regardless of file size - multi-gigabyte transfers work on 4 GB mobile devices. It is large enough that per-chunk overhead (17 bytes) remains under 0.03% of plaintext even on maximally chunked payloads.

3.4 The envelope

Each transfer carries a small authenticated-encrypted metadata blob, the envelope, or enc_header in code, containing:

The envelope is encrypted with K_file under libsodium's crypto_secretbox (XSalsa20-Poly1305), laid out as nonce (24 bytes) || crypto_secretbox_easy(header_json, nonce, K_file). This is a different primitive from the crypto_secretstream_xchacha20poly1305 used for the file body in §3.3: the body needs chunked streaming over gigabytes, while the envelope is a single small message encrypted and decrypted in one shot, which is exactly what crypto_secretbox is for. Both are standard, both are authenticated, and both are keyed by the same per-transfer K_file.

The server sees the envelope only as an opaque byte string. The recipient decrypts it after unsealing K_file (or extracting it from the URL fragment in link mode), and uses the fields to name the saved file, verify the plaintext-length invariant, and cross-check the hash the server reported.

Alongside the envelope, the sender emits an Ed25519 signature over the ciphertext hash. That is, Sign(signing_priv, blob_sha256). The recipient verifies this against the sender's signing public key (which arrives on the download response, alongside the sender's identity public key, see §5). A recipient who trusts the sender's public keys can conclude both that the ciphertext bytes are the ones the sender emitted (authenticity) and that no intermediary substituted them (integrity).

3.5 Mode split summary

Property App mode Link mode
Recipient has account Yes No
K_file delivery Sealed to recipient identity_pub via crypto_box_seal, through server URL fragment, out-of-band
Recipient identity trust Enforced via fingerprint (§5) None, anyone with the URL can decrypt
Optional password gate No (recipient is authenticated) Yes, argon2id-hashed on server
Sender signature verification Yes Skipped, link-mode sender identity is not exposed [ADR-0010]

The link-mode password does not weaken the encryption; K_file stays in the URL fragment either way. It is a rate-limited server-side check that fires before the presigned download URL is issued, defeating opportunistic link sharing without granting the server decryption capability.

3.6 Optional key backup and recovery

A device that holds the only copy of an identity key is a single point of failure. Losing a phone means losing every transfer ever sealed to it. v1.0 offered no remedy; users were told to accept the risk. That is a defensible cryptographic position and a poor product one, and in practice it pushes users toward keeping a plaintext copy of something somewhere — which is worse than a construction designed for the purpose.

Key backup is opt-in. A user who never enables it is in exactly the position described in v1.0 of this paper, and every claim made there about their keys continues to hold unchanged.

Construction. The client generates a 128-bit recovery key from the platform CSPRNG. From it, a wrapping key is derived with keyed BLAKE2b (crypto_generichash), keyed by the recovery key over the domain-separation string opaqueshare/recovery-key/v1. Both keypairs are serialised and sealed with crypto_secretbox (XSalsa20-Poly1305) under that wrapping key. The result is stored server-side as an opaque container:

"OSRK" | version(1) | nonce(24) | secretbox(identity_priv, signing_priv)

The recovery key is displayed to the user once, as 26 characters of Crockford base32 in groups of five. The alphabet omits I, L, O and U to remove the transcription ambiguities that matter when a human copies a string onto paper.

No password KDF is used. The input is already 128 bits of uniform CSPRNG output, so there is nothing for argon2id to stretch; running one would cost seconds on a phone and buy nothing measurable. This is the one place where the usual "always use a password KDF" rule does not apply, and the reason is that the secret is not a password.

What the server holds. The ciphertext container and nothing else. The recovery key is never transmitted, never derived from anything the server knows, and cannot be recovered from the blob. An adversary who obtains the entire database — §2.1 or §2.2 — gains a sealed box they cannot open.

What changes in the threat model. Two things, stated plainly:

  1. The recovery key becomes a second credential capable of recovering the identity key, and it lives wherever the user put it. A recovery key photographed, emailed to oneself, or stored in a compromised password manager is a path to the account's key material that did not previously exist. This is a real transfer of risk from "lose the device, lose the data" to "leak the recovery key, leak the data," and it is the user's choice to make. §2.6 covers the endpoint side.
  2. Rotation and backup interact. Rotating the identity key deletes the stored backup server-side, because a backup of a superseded keypair is worse than none: it would restore a key that no longer matches the published public key, and the client would have to detect that after the fact. The client compares the fingerprint derived from a restored keypair against the fingerprint the server currently publishes, and refuses a restore that does not match rather than leaving the user with a silently useless key.

What does not change. The server's decryption capability, which remains nil. Key backup adds ciphertext to the server's storage; it adds no plaintext and no key material.

4. Protocol flow

The send and receive paths are described below. The server never touches file bytes. It issues time-bounded presigned URLs against the cloud storage, and files stream directly from client to the storage provider.

4.1 Send

sequenceDiagram autonumber participant S as Sender participant API as API participant OS as Object Store S->>API: POST /transfers/initiate
{ mode, byte_count, recipient_id | link_password_hash } API-->>S: { transfer_id, upload_url(s) } Note over S: Encrypt-stream(source, K_file):
emits ciphertext chunks loop for each part S->>OS: PUT part (presigned URL) OS-->>S: 200 + ETag end Note over S: On stream close:
compute blob_sha256,
build enc_header,
sign blob_sha256 S->>API: POST /transfers/{id}/commit
{ blob_sha256, enc_header, signature,
wrapped_key?, parts? } API->>OS: HEAD storage_key
(verify byte_count) API->>API: Check blocklist by blob_sha256 API-->>S: 200 + { status: uploaded } API->>API: Enqueue recipient notification

Two aspects are worth calling out. First, the per-transfer crypto metadata (blob_sha256, enc_header, signature, wrapped_key) lands at /commit, not at /initiate (client ADR-0014 amends client ADR-0013 on this point). This is because the streaming pipeline does not know the ciphertext hash until the stream drains, and forcing the client to buffer the whole file to pre-compute it would defeat streaming's memory guarantees.

Second, the ciphertext-hash blocklist (see §8.2) fires at /commit rather than at /initiate for the same reason. The trade-off is that a banned re-upload wastes storage briefly before rejection; the TTL sweeper (§7) reclaims it. Blocklist hits are rare in practice.

4.2 Receive

sequenceDiagram autonumber participant R as Recipient participant API as API participant OS as Object Store R->>API: POST /transfers/{id}/download API->>API: Verify recipient authorised API-->>R: { download_url, wrapped_key?,
enc_header, signature, blob_sha256,
sender_identity_pub, sender_signing_pub } R->>OS: GET download_url R-->>R: Stream ciphertext,
compute rolling SHA-256 Note over R: Verify: computed hash == server-reported blob_sha256
(truncation / substitution defence) Note over R: Verify: Ed25519(sender_signing_pub, blob_sha256, signature)
(authenticity — skipped in link mode) Note over R: Unseal K_file via crypto_box_seal_open(wrapped_key,
own identity_priv, own identity_pub)
OR extract from URL fragment (link mode) Note over R: Decrypt enc_header with K_file → filename, mime, plaintext_length Note over R: Stream-decrypt ciphertext with K_file → plaintext
Verify emitted length == plaintext_length R->>API: POST /transfers/{id}/ack API->>API: Enqueue burn (see §7.1) API-->>R: { status: deleted }

Every verification step is client-side. The server never learns whether hash-verification succeeded or failed, it sees only whether /ack was called. This is deliberate: a failed verification is a security event on the recipient's device, not a coordination problem the server needs to manage.

5. Trust model and verification

Cryptography verifies that a message came from some key. It does not by itself verify that the key belongs to the person the recipient thinks they are talking to. That binding is the recipient's responsibility, and Nduzem provides three mechanisms for it.

5.1 Fingerprints

The server surfaces the sender's identity and signing public keys on the download response. The recipient's client recomputes a fingerprint (a Signal-style five-group base-10 digest of both public keys concatenated) and displays it in the receive UI. The sender sees the same fingerprint in their own profile.

Two users who trust that they are talking to each other can compare these fingerprints out-of-band, for example by voice call, video, in person, and record a durable trust decision on their local devices. This is the same TOFU-plus-verification model Signal's Safety Numbers use; the mathematics are identical, only the display format differs.

5.2 Trust on first use, with a hard block on change

When a sender first sends to a recipient (or looks up a recipient by email or handle), the client soft-warns that the fingerprint has not been verified. The user may proceed. On any subsequent send, the client checks whether the recipient's current fingerprint matches what was previously used. A mismatch is a hard block. The user must go through the verify-contact flow again before the send proceeds.

The rationale is asymmetric: sending to a wrong key is silent (the intended recipient never receives; an attacker who owns the wrong key does). Detecting key change proactively is the only way the user learns something is off before content leaves their device.

Because link-mode transfers have no on-platform recipient identity and the server does not expose the sender's public keys on the link-mode download endpoint, the signature-verification step is skipped for link mode (client ADR-0010). The web decrypt page displays a "sender not verified" banner explicitly. Users who need sender authentication should use app mode.

6. What the server can and cannot see

The following is exhaustive as of v1. New endpoints or storage columns will require this section to be revisited.

6.1 What the server sees

6.2 What the server does not see

6.3 What the server emits about itself

Three transparency mechanisms make server behaviour verifiable to external observers:

None of these prove that the server behaves correctly because a compromised operator can lie about anything they emit. What they do provide is detectable misbehaviour: any user who cares can pull the audit log and verify its hash chain against the anchored root, and any observer can note a missing canary.

7. Data lifecycle

Files exist on the object store from /commit (successful upload) through /ack (recipient signals success) or TTL expiry, whichever comes first.

7.1 Burn after read

On /ack, the server appends the transfer's storage key to a queue. A dedicated worker process drains the queue and issues DELETE calls against the object store. The queue is idempotent in that a repeated ack does not re-enqueue.

The worker is a separate container from the API service so that API request-serving latency is unaffected by burn-queue backpressure.

7.2 TTL sweeper

A second worker runs periodically and reaps two categories of lingering state:

The TTL sweeper is the backstop: the burn worker handles the common case (recipient ack), the sweeper handles crashes, abandonment, and users who close the tab without acking.

7.3 Client-side plaintext handling

The client is responsible for its own plaintext lifecycle.

Neither platform holds plaintext on disk longer than the receive session.

8. Additional protections

8.1 Rate limiting

Both per-user and per-IP sliding windows (server ADR-0019). Unauthenticated endpoints (login, register, password-reset) are IP-limited; authenticated endpoints are user-limited. Limits are documented per-endpoint in the API surface and returned to the client via Retry-After on 429 responses.

8.2 Abuse handling

Reports are first-class: any authenticated recipient can report a transfer. Admins review reports and can take down a transfer, which additionally offers to add its blob_sha256 to the ciphertext blocklist. Blocked hashes reject future re-uploads of identical ciphertext at /commit, regardless of which account submits them.

The design constraint is that abuse handling must operate over information the server has such as timestamps, storage keys, hashes, report submissions. The server does not gain the ability to inspect content in the process. Blocklist decisions are made on user reports and human review, not on server-side content inspection.

8.3 Log hygiene

Application logs never contain plaintext PII. Structured logging redacts email, handle, and any other application-layer-encrypted field at the emit point. A dedicated test suite exercises the receive path with fixture PII and asserts none of the fixture strings appear in captured log output.

8.4 GDPR and DSAR

Right-to-erasure requests trigger a tombstone-plus-scrub flow. The user's account row is retained (foreign keys from historical transfers require it) but every PII field is overwritten with a per-row nonce. A signed erasure receipt is returned so the user has evidence the request was honoured.

The following rubric compares Nduzem's design position against existing options. Ratings reflect the systems' stated designs, not audits.

System E2E for file bytes Open protocol Files-first UX Recipient identity trust model
Nduzem Yes Yes (this paper) Yes TOFU + fingerprint (§5)
Signal Yes Yes No (chat-first; file transfer is a Signal message) Safety numbers
WeTransfer / Dropbox Transfer No No Yes N/A (provider sees plaintext)
Firefox Send (2017-2020) Yes Yes Yes None, link-only
Tresorit / SpiderOak Yes (claimed) No Yes Proprietary
MEGA Yes Partial Yes Account-scoped
ProtonDrive share Yes Partial Yes Account-scoped

"Open protocol" here means the wire protocol and cryptographic construction are publicly specified, enough for a third party to build a compatible implementation or audit the design. Separately, Nduzem publishes its client source code; the server code is not open. Under a zero-knowledge design the client is where the audit-relevant work happens: the server is not trusted with content by construction, and its correctness is instead constrained by the transparency mechanisms in §6.3.

9.1 Signal

The design borrows Signal's Safety Number pattern for fingerprint display and the underlying primitives (X25519, Ed25519, XChaCha20 via secretstream). What Nduzem adds is files-first UX: per-transfer keys rather than session-persistent state, link-mode delivery for recipients without accounts, and integrity metadata sized for multi-gigabyte files. What it lacks compared to Signal is forward secrecy at the message level; each K_file is one-shot but identity keys are long-lived.

9.2 Firefox Send (2020, closed)

Firefox Send pioneered the K_file-in-URL-fragment pattern that link-mode inherits. Its closure was driven by abuse handling: the combination of easy sharing, provider-blind content, and no per-user rate limiting made it a preferred vector for malware distribution [2]. Nduzem's abuse-report and ciphertext-hash blocklist mechanisms (§8.2) are direct responses to this failure mode. Blocklist decisions are made on user reports, not server-side content inspection, so the zero-knowledge property is preserved.

9.3 Tresorit / SpiderOak

Both make strong end-to-end claims but ship closed-source clients, so users cannot independently verify that the code doing the encryption on their device matches the vendor's stated protocol. Under a zero-knowledge design, the client is the load-bearing surface for that claim, the server, by construction, is not trusted with content, so its source-code status is a lesser concern for confidentiality. Nduzem publishes its client source, so a user or auditor can read the exact encryption path that runs on their device against this paper's specification. The server is not open source; its role in the security argument is limited to what §6 enumerates, and the transparency mechanisms in §6.3 exist precisely to make server behaviour auditable without requiring source access.

9.4 MEGA

Backendal, Haller, and Paterson (2022) published a series of practical cryptanalytic attacks against MEGA's client-side crypto [4], including a malleability attack that could be turned into a plaintext-recovery attack under specific server behaviour. Nduzem's design differs in ways relevant to those attacks: it uses libsodium primitives rather than custom AES-CCM code, it signs blob_sha256 explicitly (not a derived value that admits malleability), and it does not depend on server-side unwrapping of per-file keys (crypto_box_seal is unseal-only on the recipient side).

The comparison is not "Nduzem cannot be broken." It is "Nduzem's construction avoids the specific classes of attack that broke MEGA in 2022, and its ADR trail makes future audit against similar attack classes possible."

9.5 ProtonDrive share

Proton's file-sharing product uses OpenPGP-based end-to-end encryption with account-scoped recipient identities. It is a credible alternative for users already inside the Proton ecosystem. Nduzem's differences are the streaming primitive (secretstream vs. per-message OpenPGP), link-mode support with no account required, and the open-protocol commitment (this paper).

10. Non-goals and future work

The following are explicit non-goals in v1, listed so users can make informed decisions:

11. References

Cryptographic primitives:

Prior art:

Nduzem ADRs cited in this paper:

Client repo (public):

Server-side design records are internal to the project. The security-relevant behaviour of the server (typically what it sees, what it does not see, and the transparency mechanisms that make its actions auditable) is fully described in §6 of this paper; a reader who wants to verify a claim can do so against the paper itself. Access to the internal design records is available under NDA on request; contact the project.


License

This paper is licensed under Creative Commons Attribution 4.0 International. You may share and adapt it, including commercially, with attribution.

Changelog