Overview
Dynodoc keeps a record of who changed what in a document, so most of its security work protects that record and the access to it. This page says what is in place, what exists only in source code so far and what is not claimed. It is written from the project’s own engineering documents and deployment configuration; where something has not been verified it says so.
- Changes go into an append-only, hash-chained log that the database refuses to edit, and the service’s own database login cannot rewrite or delete it.
- Sessions, editor connection keys and signing keys are scoped, expiring and revocable.
- Pages are served over HTTPS with a strict content security policy and no third-party scripts.
- There is no external audit, no certification and no independent witness of the history yet. The limits are listed below rather than left for you to find.
What is live today
As of 12 October 2026 the hosted service runs the controls marked Live. Those marked In source are implemented and tested in the code but not yet deployed. Those marked Not built do not exist.
| Control | What it does | State |
|---|---|---|
| Hash-chained, append-only history | Each change event is linked to the previous one with SHA-256. Sequence gaps are checked, and the database blocks UPDATE and DELETE of events. | Live |
| Checked snapshots | Snapshots carry immutable receipts. Reads replay the event chain and compare the full state, and fail closed on a missing or mismatched receipt. | Live |
| Restricted database login | The API connects with a role that cannot alter history, disable triggers, change schemas or elevate itself, and refuses to start with an owner or superuser role. Migrations run separately, by the operator. | Live |
| Revocable sessions | Signing out revokes the login on the server, not only the cookie. | Live |
| Project rules and metadata audit | Project rules set a floor of protection and approvals for every file. Changes to titles, settings and archive state are recorded with before and after digests. | Live |
| Browser policy | Nonce-based content security policy, HSTS over HTTPS, no MIME sniffing, strict referrer policy and restricted framing. | Live |
| Request and workload limits | Bounded concurrent responses, per-identity read budgets, SQL timeouts, a 16 MiB request cap, 3 MB file uploads and proxy rate limits on sign-in. | Live |
| Hardened containers | Web, API and converter run with read-only file systems, no Linux capabilities and memory, CPU and process limits. The database, API and converter are not publicly reachable. | Live |
| Audited erasure | An administrator can erase a whole document, leaving a minimal receipt. No other operation removes canonical history. | Live |
| Checked backups | PostgreSQL archives are validated before they are kept, for 14 days, on the same server, so they are not independent recovery. | Live |
| Published source | The source archive of the deployed revision is downloadable from /source. | Live |
| Account signing keys | An Ed25519 public key is bound to your account by proving possession of the private key. Revocation is permanent. | Live |
| Signed project history and branch receipts | Signed commits, protected main branch, reviews bound to an exact candidate, and signed receipts issued with each accepted branch update, verified against a pin kept on the client. The schema and API are deployed; publication stays refused until the operator installs a receipt-signing key. | Live, not enabled |
| Durable storage budgets | Per-document and per-account limits on retained shared objects. | Live |
| Independent witness of history | Signed heads kept by someone other than the service, to expose a rewrite. | Not built |
| Off-site backups | A second copy that survives loss of the server. | Not built |
What the history guarantees
Each event stores a SHA-256 hash of its type, target, payload and actor, plus a chain hash that combines it with the previous event. Altering, removing or reordering an earlier event changes every later chain hash, which verification detects. Verification, available through the API and the operator tool, replays the chain from the first event and rebuilds each stored snapshot to compare it with the stored state, so a corrupt snapshot cannot hide behind a healthy later one.
This is a consistency check, not a proof of truth. The first hash version does not cover the document identifier, event identifier, sequence number or creation time. Receipts on snapshots are internal attestations, not signatures. Someone who controls the whole database can disable triggers and rewrite both the history and its checks, and the hash chain alone cannot expose that until the history is committed to something outside the database. Signed and independently witnessed heads are planned and are not in place.
Restoring, reverting or deleting content adds new events. The only removal is an administrator’s erasure of a whole document, which keeps a minimal receipt; see what can and cannot be erased.
Access, sessions and keys
- Sign-in: an emailed link valid for 15 minutes, or Google sign-in limited to identity, email and profile. The session is an encrypted, HTTP-only cookie for up to 30 days whose login can be revoked on the server; an open event stream stops before its next protected event once the login is revoked.
- Permissions: roles are checked again on every read and write, and project rules set a minimum that file rules cannot weaken. A published link or a content hash never grants access to private history.
- Editor connection keys: 32 random bytes made in the add-in, of which Dynodoc stores only a hash. A key reaches one file and one editor, can read and propose but not merge or grant access, expires after seven days and can be revoked at once.
- Signing keys (in source): enrolment needs a five-minute, single-use challenge signed by the private key, which never leaves your device. Challenge requests and active keys per account are limited.
Hosting and network
- Transport: HTTPS only, with HTTP redirected and TLS 1.2 or 1.3 on the workspace. HSTS lasts one year and is not set for subdomains or preloaded.
- Scripts: only from Dynodoc’s own origin with a per-response nonce, no inline script or eval, and no third-party analytics, fonts or CDNs on the website. The three Office panes additionally allow Microsoft’s Office.js hosts and may be framed by Office.
- Limits: at most 64 ordinary and 256 streaming responses in flight; 120 expensive reads per identity per minute with two active at a time; a 15-second SQL statement timeout. Sign-in routes are limited to 60 requests per minute per address, with a short burst allowance.
- Logs: web server access logs record address, time, method, path (without the query string, so sign-in tokens and OAuth codes are not logged), status and size, and mask private link paths.
- Backups: validated PostgreSQL archives, kept 14 days on the same server. Restores are exercised into a fresh database, never over the live one.
Limits and what we do not claim
- No independent assurance. Dynodoc has no security certification such as SOC 2 or ISO 27001, no independent security audit or penetration test, and makes no claim of compliance with GDPR, HIPAA or another regulation.
- Not end-to-end encrypted. The service reconstructs and reviews versions on the server, so the operator can read content. A particular encryption-at-rest configuration is not claimed.
- The operator is trusted. The database owner and credential rotation are trusted operations. Startup checks cannot stop an operator from changing privileges later, and the permitted audited erasure path remains available to administrators.
- One server, no uptime promise. Backups share the server with the live system, so they are not independent recovery. There is no service-level agreement. Workload limits are per process, allow a burst at window boundaries and do not make search indexed or stop all CPU-heavy work.
- Add-ins are pre-release. They are not in a marketplace, and acceptance testing in installed Word, Excel, PowerPoint, Docs, Sheets and Slides is still to be done by the owner. They record observations at snapshot times and do not prove who typed a change.
- Signed history needs more. Even with signed receipts, a server holding its signing key can show different histories to different clients until independent witnesses exist.
Report a vulnerability
Please report a suspected vulnerability privately and do not publish it. Use GitHub private vulnerability reporting for the public Dynodoc engine repository; it is the only reporting channel with a confirmed owner today, and reports about any part of Dynodoc, including this website and the add-ins, are welcome there. A dedicated security email address is awaiting confirmation.
- Reporting channel
- GitHub private vulnerability reporting
- Security email
- Awaiting confirmation.
- Machine-readable
- /.well-known/security.txt
Include the affected address or revision, the impact, a minimal reproduction that uses synthetic data and any suggested fix. Test only against your own account and data. Do not publish credentials, private documents or a working exploit, and do not run denial-of-service or volume tests. Areas of particular interest are authorization across drafts and public views, event integrity and replay, parser and resource limits, authentication, administrative erasure and SQL concurrency.
This is an early project: fixes target the current main branch and no response time is guaranteed. No legal safe harbour is offered by this draft; the operator must decide that with its final terms. See also the acceptable use page.