Security at qub
Effective date: 23 September 2026 Version: 1.1 — implementation-accuracy review
For researchers — quick reference:
- Where to send reports: support@qub.social with the subject prefix
[SECURITY].- What to include: the vulnerability, steps to reproduce, and any proof-of-concept.
- Our response: we acknowledge receipt within 3 business days and aim to ship a fix within 90 days.
- Safe Harbor: we will not pursue legal action against good-faith research that follows the rules in §12 (no access to data that isn't yours, no service degradation, no retention of obtained data beyond what's needed to demonstrate the issue, give us a reasonable disclosure window).
Full details are in §12 (Coordinated Disclosure).
Who We Are
qub.social is operated by VSPRY AUSTRALIA PTY LIMITED (ABN 41 631 026 330), Level 38, 71 Eagle Street, Brisbane QLD 4000, Australia. References to "qub", "we", "us", and "our" mean that entity.
Security contact: support@qub.social with the subject prefix [SECURITY].
1. Our Approach
qub is trust infrastructure. The product is worthless if it is not secure, so security is not a feature — it is the substrate. This page describes, in concrete terms, how we safeguard our stack, your data, and the integrity of sealed content.
The value of a verifiable temporal commitment grows as more of the internet becomes machine-generated. A verified storage transaction or transparency-log anchor can establish that ciphertext existed no later than its block time; the sealed artifact separately proves content integrity, drand-round binding, and any authorship signatures. Keeping those claims distinct is the bar this page is held to.
We do not ask you to trust us. We design so that the trust required of us is as small as possible, and where trust is required we explain exactly what is being trusted and why.
Three principles drive every design decision:
- Minimise what the server can see. In the default browser message flow, plaintext and the wrapper key stay on your device. Server-side Builder sealing, pact co-signing, and explicitly enabled recovery have different trust boundaries, disclosed below. Where we hold metadata, we limit it to what the selected feature needs.
- Make compromise locally contained. A breach of any one component (our server, the email provider, a drand node) should not reveal sealed content that has not yet reached its reveal time.
- Make the protocol auditable. The sealed artefact is verifiable end-to-end with public cryptography. You do not need to trust qub the service to verify a qub the artefact.
2. Threat Model
2.1 What We Protect Against
- An attacker who gains read access to our stored server-side data before reveal time. In the default private browser flow they obtain metadata and opaque wrapped bytes, not plaintext or K. This protection does not apply to a K explicitly retained for recovery, to public/bare delivery after its drand round, or to plaintext transiently supplied to the Builder
/api/v1/sealand pact workflows. - An attacker who intercepts traffic between your browser and our infrastructure. TLS terminates at our CDN edge; sealed payloads are already encrypted before transit.
- An attacker who tampers with a stored payload. Outer-wrapper authentication (when present), canonical decoding, the body hash,
qub_idre-derivation, round binding, and optional signatures make tampering fail verification; the viewer refuses to render it. - An attacker who tries to bind a forged author email to a signing key. Email attestation requires possession of both the private signing key and a one-time code delivered to the email inbox.
- A compromised drand beacon operator. The drand network uses threshold BLS signatures across multiple independent operators; a minority cannot forge early-release signatures.
2.2 What We Cannot Protect Against
We are honest about our limits. qub cannot defend against:
- A compromise of your device before you seal. Local keyloggers, malicious browser extensions, or physical access to an unlocked device can capture plaintext at the point of composition.
- A collapse of the drand threshold. Multiple independent organisations run the drand network specifically to make this difficult, but it is not cryptographically impossible: if enough operators collude, they could derive timelock keys early.
- The release properties of a valid copy. A public/bare qub becomes decryptable after its drand round. A private/wrapped qub additionally requires K; anyone who obtains both the stored bytes and K can decrypt after the round. Permanent-storage and anchored-log records cannot be recalled merely by removing them from qub's product surface.
- A global adversary who breaks the underlying cryptography (AES-GCM, BLS12-381 pairing assumptions, SHA3-256, ML-DSA-65). If these primitives fall, the cryptographic ecosystem at large has larger problems.
3. Client-Side Cryptography
In the default browser message flow, content encryption happens before the upload request. Two explicit paths differ: Builder /api/v1/seal deliberately sends plaintext and caller-generated K to the Worker for in-memory sealing, and pact staging/co-signing sends the signed structured pact to the service so it can finalise the bilateral artifact. Neither exception should be mistaken for browser-path end-to-end encryption.
3.1 Timelock Encryption
qub uses tlock — identity-based encryption keyed to a future drand beacon round. Encryption proceeds in your browser using the drand network's public key; the decryption key is released publicly by the drand network only when the target round is reached. Nobody, including us, can reconstruct the decryption key ahead of time.
We target the quicknet chain:
- 3-second round period
- Unchained mode (each round is independent)
- BLS12-381 G1 signatures
- Chain hash
52db9ba70e0cc0f6eaf7803dd07447a1f5477735fd3f661792ba94600c84e971
The quicknet chain's public key and genesis time are compiled into the client. We do not fetch chain parameters at runtime, so a malicious node cannot substitute a chain we control.
3.2 Symmetric Encryption
The tlock scheme wraps an AES-256-GCM content key. AES-GCM provides authenticated encryption: a single bit flipped in the ciphertext causes decryption to fail, rather than producing silently-corrupted plaintext.
3.3 Canonical Serialisation
Protocol structures are serialised using deterministic CBOR (RFC 8949 §4.2 core deterministic encoding). Two implementations encoding the same logical structure produce identical CBOR. Complete sealed payloads are not deterministic: tlock and outer-wrapper encryption use fresh randomness. The body hash is computed over the raw body bytes, while canonical encoding makes the surrounding signed/wire structures unambiguous.
We wrote the CBOR encoder by hand for both our client and server implementations rather than relying on a generic serialisation library — the requirement is exactness, not ergonomics, and property tests run in both implementations to verify they agree.
A regression test asserts the canonical wire format contains no qub-brand byte sequence beyond the protocol-primitive qub_id field key. The wire format is intentionally brand-agnostic — any conforming viewer (ours or a third party's) can render any qub from permanent storage, regardless of which deployment sealed it. The test is a tripwire that prevents a future change from accidentally baking a brand reference into bytes that, once in permanent storage, cannot be rewritten.
3.4 Body Hashing and Pre-Reveal Integrity
Every sealed payload carries a SHA3-256 hash of its raw body bytes. The hash is bound into qub_id and, when authorship signing is enabled, into the V2 signature input. A viewer recomputes it after decryption and rejects a mismatch.
The 32-byte content identifier qub_id is derived from a 108-byte preimage covering the protocol version, content type, created and unlock timestamps, optional outcome timestamp (or its zero sentinel), target drand round, body hash, and SHA3-256 of the optional NFC-normalised title. A gateway or CDN cannot change any bound field consistently and still pass re-derivation. Titles are capped at 100 NFC code points and rejected for the shared hostile/control-codepoint class (including bidi overrides, zero-width characters, the tag block, BOM, C0, C1, and DEL).
3.5 Signing (ML-DSA-65)
Authorship signing uses ML-DSA-65 (FIPS 204), a NIST-standardised post-quantum signature scheme. We deliberately chose a post-quantum primitive for signing because sealed content is permanent: a signature that verifies today must still verify decades from now, including after large-scale quantum computers become practical.
Signing keys are generated in the browser. The local secret is wrapped under a non-extractable WebCrypto key before IndexedDB storage. If the account-scoped cross-device recovery feature is used, an AEAD-encrypted portable key blob is stored server-side; its secret-key ciphertext is bound to the immutable account id, and the service validates the public envelope but cannot decrypt the secret material. Raw private-key bytes are not sent to the server. Public keys and attestation records are stored for verification and identity display.
The same in-browser tlock decryption applies inside the qub embed: when a sealed qub is rendered through <qub-embed> on a third-party page, decryption still happens in the embed iframe in the viewer's browser. The embed does not change the trust model — plaintext is never decrypted on a qub server.
3.6 Public Attribution — Opt-In
Sealed qubs carry no on-chain pointer to their creator unless the creator explicitly chooses to attach one. When you seal a qub the reference app emits an Author storage tag (a 64-char hex fingerprint of your signing public key) only when "Public attribution" is enabled on the date-picker step. With the toggle off — the default — no Author tag is written and the qub is unattributed in permanent storage: nothing in storage links the upload to your handle, your email, or your other qubs. With the toggle on, the fingerprint resolves to your @handle via the attestation chain in §6.3 / §10 and the viewer countdown shows "Sealed by @{handle}" before reveal.
This is a deliberate guard against the enumeration risk an always-on Author tag would create: a third party who learns a creator's fingerprint could otherwise search permanent storage by the tag and reconstruct that creator's full historical output. Opt-in attribution closes that channel — only qubs the creator explicitly chooses to attribute appear under a fingerprint in permanent storage.
The /u/{handle} profile page is a verified-identity card — handle, optional display name + URL, "verified email" pill (no address), and the cryptographic fingerprint short form. It does not list a creator's qubs. Visitors who want to see a specific qub from a creator follow that qub's delivery URL directly.
3.7 Outer Encryption Wrapper
Even after timelock decryption is mathematically possible—once the drand signature for the bound round has been published—the canonical timelock layer alone would let an indexer bulk-decrypt discoverable qubs. Private delivery closes that channel with an additional symmetric layer around the timelock-encrypted bytes (Protocol §13). Public delivery intentionally omits the wrapper so notification, embed, and discovery links can work without a secret fragment.
The wrapper uses AES-256-GCM, a NIST-standardised authenticated cipher, with a fresh 256-bit key K generated per qub by your browser's CSPRNG. K is bound to the qub's qub_id as authenticated additional data, so a key from one qub cannot be reused to decrypt a different qub.
K never reaches our servers in the default private browser flow. It is encoded into the URL fragment of the share link (https://qub.social/c/<tx_id>#<base64url(K)>). Browsers do not transmit URL fragments to servers—RFC 3986 places the fragment outside the request—so qub.social, storage gateways, CDNs, and request monitoring are blind to K in that flow. The stored OuterWrapper is recognisable structured CBOR, but its authenticated ciphertext field hides the inner SealedQub structure and cannot be opened without K.
Net consequences:
- qub.social cannot decrypt default private browser seals from stored data alone. A data-store compromise reaches opaque ciphertext without K. Public qubs and recovery-enabled qubs have different exposure by design.
- Fragment loss is unrecoverable without an opted-in recovery channel. If you save a private link without the fragment and did not enable recovery, the qub becomes unreadable through that link. The seal flow surfaces an explicit "save this URL" disclosure for this reason.
- Opt-in recovery. When you opt into creator lifecycle emails for a qub AND the email matches your verified identity, we accept K with the upload, store the full delivery URL on your identity's sealed-history record, and use it as the link in the seal-confirmation email. This trade — a server-side recovery channel in exchange for some end-to-end purity — engages only on explicit opt-in and only for that qub. The default posture is crypto-shredding.
The Worker's server-side /api/v1/seal endpoint (used by AI agents and other API callers) requires the caller to generate K with a CSPRNG, retain it locally, and supply it as wrapper_key_b64url. The Worker necessarily sees both plaintext and K in memory on this explicitly trusted path, but persists neither. A mandatory Idempotency-Key prevents a lost response from creating a second billed qub, while the caller-retained K can be combined with the replayed fragment-less URL. This differs from the default browser path, where K never reaches the Worker unless the creator explicitly enables recovery.
4. Transport and Edge
4.1 TLS
Browser traffic to qub is served over HTTPS at the Cloudflare edge. Responses set HTTP Strict Transport Security (max-age=63072000; includeSubDomains; preload). The exact negotiated TLS version and cipher suite are governed by the active edge configuration rather than asserted by application code. We do not expose a separately reachable origin server.
4.2 Content Security
The compiled client is served with strict content-type and cache headers. The SPA shell is a single origin. We do not embed third-party scripts for analytics or advertising. The two third-party touchpoints in the product are both narrowly scoped: the purchase flow leaves the SPA entirely with a full-page redirect to Stripe-hosted checkout (https://checkout.stripe.com/…) — Stripe's UI never executes in our origin and we never see card data — and the seal flow loads Cloudflare's Turnstile widget, a privacy-preserving CAPTCHA alternative that Cloudflare renders inside its own sandboxed iframe. Neither party can read the rest of the page.
The qub embed iframe (served from qub.social/embed/{tx_id} and loaded into third-party sites by embed.js) carries its own Content-Security-Policy. Its connect-src allowlist is 'self', https://qub.social, https://arweave.net, https://ar-io.dev, https://permagate.io, https://api.drand.sh, and https://drand.cloudflare.com. The iframe runs with sandbox="allow-scripts allow-top-navigation-by-user-activation" (not allow-same-origin): the host page cannot read its DOM, and it cannot navigate the host except after a user action.
4.3 CORS and Fetch Scope
The browser client makes fetch requests only to:
- Our own API (
api.qub.socialand staging equivalents) - Storage gateways (read-only, for wrapped-private or bare-public byte retrieval — §3.6)
- drand beacon endpoints (read-only, for reveal-time round signatures)
The embed's destinations are enforced by its CSP. The main SPA's intended destinations are fixed in code and configuration and are exercised by browser and integration checks; Subresource Integrity is not a network-destination control.
The embed fetches stored bytes through the allowlisted qub/storage origins, unwraps private payloads in-browser using K from its URL fragment, and fetches reveal-time round signatures from the two allowlisted drand origins. The main SPA uses the four-endpoint fallback set in config/drand-endpoints.json (drand.cloudflare.com, api.drand.sh, api2.drand.sh, and api3.drand.sh) so one endpoint outage does not block reveal. The embed CSP denies connections outside its explicit list.
5. Server-Side Infrastructure
5.1 Serverless Edge
Our API runs entirely on a managed serverless runtime at the edge. There are no VMs, no containers, and no persistent server processes we administer. This dramatically reduces the attack surface we are responsible for: we do not run an OS, a web server, or an application runtime that we must patch.
A separate public-CORS middleware applies Access-Control-Allow-Origin: * to the following implemented path set: /embed.js, /embed/v1.js, everything under /embed/; /api/v1/telemetry; /api/v1/openapi.json; everything under /api/v1/qub/ (including bytes, metadata, proof, engagement, notify, and push subroutes); everything under /api/v1/log/; public handle lookups under /api/v1/handle/; and public avatar reads under /api/v1/identity/avatar/. Its preflight permits GET, POST, and OPTIONS with the Content-Type request header. This prefix-based surface is broader than only the calls the embed currently makes, so every handler below those prefixes must continue to enforce its own validation, authentication, rate limits, and abuse controls. Other API paths retain the qub.social-restricted CORS policy.
5.2 Storage
- Metadata and coordination stores hold identity and attestation records, entitlements and billing references, API-key records, denylist entries, sessions, idempotency state, queues, and rate-limit / concurrency state. Different consistency needs use KV, D1, and Durable Objects rather than one universal store.
- Our object store is also a durability substrate. It holds the exact wrapped or bare qub bytes acknowledged by upload, transparency-log leaves and coordinate-keyed Merkle nodes, anchor material, structured event logs, and response/metadata caches.
- Permanent public storage holds transparency-log anchors and, for the T3 path or deferred publication, individual qub transactions. We do not operate that network. Private browser payloads remain opaque there unless the holder also has K; public/bare payloads deliberately do not have that extra link-capability layer.
The default browser message flow does not persist plaintext on qub infrastructure. Builder /api/v1/seal handles plaintext and K in memory but persists neither. Pact staging necessarily stores the signed structured pact until it is co-signed, retracted, or expires. Opt-in recovery stores a delivery capability (the full fragment-bearing link) so it can be recovered later. We therefore do not describe the whole storage tier as “metadata-only.”
5.3 Secrets
Secrets (signing wallets, provider tokens, and HMAC keys) are supplied through platform secret/environment bindings rather than source control. Runtime components receive only the bindings they need. Rotation and overlap procedures are component-specific; we do not claim a single universal automatic or audited rotation mechanism.
5.4 Logging and Telemetry
Structured JSON logs are written on every API request with a correlation ID surfaced in the X-Request-Id response header. Client telemetry is anonymous — no device identifier, no IP address, no content preview. Events are buffered in memory and flushed on a best-effort basis; a failed flush is discarded, not retried. Telemetry is designed to be disableable at the network layer without affecting the product.
6. Authentication
6.1 Magic-Link Sign-In
Sign-in uses a single-use, HMAC-signed token delivered to your email inbox. The link is valid for 15 minutes and redemption is atomically claimed so concurrent or replayed use fails closed. On success the browser receives an opaque __Host-qub_session cookie with Secure, HttpOnly, SameSite=Strict, and Path=/ attributes.
Sessions have a 30-day idle limit and 90-day absolute limit, rotate after 24 hours, and accept only the immediately previous generation for a 120-second lost-response grace period. Sensitive account mutations require authentication within the preceding 10 minutes. The HMAC signing secret is a platform binding; a metadata-only read does not by itself mint a valid token.
6.2 API Keys (Developer Tier)
Developer API keys use the prefix qub_sk_ for easy recognition and grepability. Each key:
- Is bound to an account, scopes, and an optional IP CIDR allowlist
- Is shown in raw form once; persistent records retain its SHA-256 hash, not the bearer secret
- Can be rotated with a one-hour grace mapping in which the old key resolves to the replacement
- Has independent quota and rate-limit state
- Is never logged in full; logs record only the key identifier
Admin key-management endpoints are gated behind a separate admin credential.
6.3 Email Attestation (Authorship Signing)
Binding an email address to a signing key requires:
- Possession of the private signing key (you sign a challenge)
- Possession of the email inbox (you enter a 6-digit code delivered by email)
Either alone is insufficient. Revocation is a signed record on your own account and takes effect immediately; viewers fetching the attestation see the revoked state and display accordingly.
7. Payments
Card entry and processing run inside Stripe-hosted checkout. We never receive card numbers, expiry dates, or CVCs. We do store Stripe customer and subscription identifiers, subscription state, and period data on entitlement/API-key records so access, renewals, metering, cancellation, and refunds can be reconciled. Stripe's privacy and security statements govern its handling of payment data.
The seal endpoint cross-checks the entitlement record against the device identifier and, for signed-in users, against the linked identity. An entitlement cannot be reused across devices without the user explicitly restoring it via magic-link sign-in.
8. Abuse Resistance
8.1 Bot Detection
The seal flow is gated by a privacy-preserving CAPTCHA alternative that does not use cookies for tracking and does not fingerprint for advertising. A failed challenge is rejected by our edge Worker before any seal-side processing happens.
8.2 Rate Limiting
Rate limits are enforced at several layers:
- Per-IP and per-key limits on seal, read, and auth endpoints
- Per-email limits on magic-link requests (prevents mailbox flooding)
- Per-counter-party limits on pact invite emails (ten per recipient address per UTC day, the primary spam-relay mitigation; honest pacts almost never approach the cap)
- Per-IP limits on telemetry submission
Counters and atomic claims are distributed across KV, Durable Objects, and platform rate-limit bindings according to the endpoint's consistency requirements. Rate-limited requests return 429; endpoints that can calculate a retry window include Retry-After.
8.3 Content Moderation
The default browser-upload route cannot scan the body: it receives only the client-sealed artifact. The Builder /api/v1/seal route sees plaintext transiently, and pact staging holds structured terms until finalisation, but those trust exceptions do not turn the general byte-blind upload path into a content scanner. Operational moderation is a denylist at the viewer layer: a denylisted qub is refused by our viewer regardless of whether the stored payload remains reachable. Denylisting does not retract durable bytes, transparency-log entries, or permanent-network data already published.
Abuse reports are rate-limited using a one-way hash of the reporter's IP; we do not store IPs in the clear for this purpose.
9. Supply Chain and Build Integrity
9.1 Toolchain Pinning
Compiler and runtime versions are pinned in repository configuration and dependencies are resolved through committed lockfiles. CI checks generated-file freshness and reproducibility-sensitive invariants. We do not make the stronger claim that every clean build is bit-for-bit identical across all supported machines.
9.2 Lints and Static Analysis
The workspace enables our strictest lint groups at the deny level. CI treats every warning — including documentation-link warnings — as a build failure. This is deliberate: we use the lint strictness as a tripwire for subtle regressions.
9.3 CI Gates
The CI workflow covers formatting and strict lints; Rust, WASM/browser, Worker, embed, and API tests; type checking; code coverage; mutation/invariant checks; dependency and workflow static analysis; i18n keys, coverage, drift, and hostile-codepoint checks; generated-doc/API/knowledge-base freshness; document inventory and internal-link checks; stylesheet and bundle budgets; and OpenAPI validation. Some expensive mutation jobs are scheduled rather than run on every push.
A single required ci roll-up remains red if any required job fails. Protected-branch and deploy workflows consume that result rather than duplicating a smaller security gate.
9.4 Mutation Testing
A weekly job runs mutation testing against the security-critical pure modules: hashing, canonical CBOR, seal, unlock, the wire-format newtypes, the protocol type validators, and the handle namespace. Mutation testing answers "does our test suite catch subtly wrong code?" — if a mutated implementation still passes all tests, we know we have a test-coverage gap and address it.
9.5 Git Hooks
Local hooks (pre-commit, pre-push) mirror the CI gates so regressions are caught before they leave the developer's machine. Hooks are installed via a repo script; they are not bypassed in our workflow and the CI is the authoritative gate if they are skipped.
10. Testing
Security-critical code carries three kinds of tests:
- Unit tests verify expected behaviour on known inputs, including test vectors derived from the protocol specification.
- Property tests generate thousands of arbitrary inputs and assert invariants: canonical CBOR round-trips, signature verification round-trips, email-binding predicates, pact acknowledgement determinism.
- Cross-implementation tests verify that our client and server implementations agree byte-for-byte on canonical encodings. This catches divergence between the two implementations before it reaches production.
11. Branch and Release Hygiene
Feature branches advance staging only through a Gate 1 pull request: required ci green, no unresolved change request, no merge conflict, and a clean reviewed tree; the merge is squash-and-delete. main advances only through the Gate 2 staging → main pull request and preserves ancestry with a merge commit. Direct branch pushes are not the release workflow.
Staging and production deploys are triggered from the corresponding protected staging and main branch states after CI. Pull-request code and fork credentials do not receive deploy secrets.
Secrets used in deploy workflows are scoped to the deploy environment by our CI platform. They are not available to pull-request workflows from forks.
12. Coordinated Disclosure
If you believe you have found a security vulnerability in qub, we want to hear about it quickly and we commit to handling the report professionally.
- Email
support@qub.socialwith the subject prefix[SECURITY]. - Describe the vulnerability, steps to reproduce, and any proof-of-concept.
- Give us a reasonable disclosure window (typically 90 days) before going public.
- Do not access data that does not belong to you, degrade service for other users, or retain data obtained during research beyond what is necessary to demonstrate the issue.
We acknowledge receipt within three business days and keep you informed as we investigate. With your consent, we credit reporters in release notes.
12.1 Safe Harbor
If your research follows the rules above (good-faith investigation, no harm to other users or the service, reasonable disclosure window), we will not pursue legal action against you, and we will not ask law enforcement to. We treat your work as authorised testing and we'd rather you find the bug than someone else.
This Safe Harbor applies to:
- Research on the live qub.social service (not on test fixtures we publish for that purpose).
- Reverse-engineering of our published binaries and the open-source qub-core / qub-app crates.
- Any vulnerability class — protocol, application, infrastructure, supply-chain — that affects qub.
It does not apply to social engineering of qub team members, denial-of-service tests, or accessing other users' data beyond what's needed to demonstrate the issue. If you're unsure whether something falls inside the Safe Harbor, ask first using the same [SECURITY] subject prefix.
13. Honest Limitations
Security is a practice, not a state. Some limitations are worth naming directly:
- We are a small team. Our review depth does not match a large corporation's dedicated application-security function. We compensate with strict automated gates and a minimal attack surface, but we do not claim infallibility.
- The permanence of our storage backend is a one-way door. If a mistake causes sealed content to become decryptable earlier than intended, we cannot undo it. We treat the seal flow with commensurate care.
- The drand network is an external dependency. A catastrophic failure of drand would affect every qub's reveal behaviour. We monitor drand health and have contingency documentation for chain migration if required. For unlock dates more than 2 years out, the seal-time confirmation modal shows an explicit disclosure: long-horizon qubs depend on drand chain durability, and a future drand chain migration may require recovery steps to unlock the qub. For unlock dates more than 5 years out, you must check an extra box confirming you've read and accept this risk before the seal proceeds.
- Cryptographic primitives we rely on are standardised and widely reviewed, but cryptography evolves. Where we have choices (post-quantum signing, authenticated encryption), we pick the more conservative option.
14. Changes to This Page
Material changes are noted by updating the effective date at the top. Where a change reflects a concrete security improvement, we describe it briefly in the public changelog. Where a change reflects a policy clarification, we describe what changed and why.
For questions about anything on this page, email support@qub.social with the subject prefix [SECURITY].
15. Change Log
| Version | Effective date | Summary |
|---|---|---|
| 1.1 | 23 September 2026 | Reconciled cryptographic claims, delivery modes, storage, CSP, sessions, API keys, payments, CI, and release workflow with the implemented system. |
| 1.0 | 2 May 2026 | Initial publication. |