Home / For evaluators
Security, architecture, procurement
For evaluators
This page is for the person who has to say yes or no. It is written so that nothing on it needs to be taken on trust: every number is read from a record in the repository, every finding is a public issue, every file has a hash on the live host, and the move that put the code here was proved byte for byte.
The architecture in four sentences
There is no server of ours: the reader code is static files on a host that cannot run code, and the pages that use it are static too. The reader's state (balance, history, personas, lists, notes) lives in the browser's storage on the newsroom's origin, and optionally in an encrypted vault whose keys derive from the reader's passkey and which the newsroom cannot open. Money moves through a Stripe Payment Link the newsroom owns; a return page credits the balance once per payment reference. Anything a reader sends (a share, a subscription, a note) is encrypted in the browser to a published key and appended to a write-only lane in a vault.
Trust boundaries
| Party | Sees | Cannot |
|---|---|---|
| The reader's browser | everything: the state is theirs, readable, exportable, editable | be stopped from editing their own balance (accepted; see the security model) |
| The newsroom (static host) | page requests, as any host does | see a balance, a persona, a list or a note; verify a payment |
| This host (static files) | requests for the code | run code, set headers, see any reader data |
| Stripe | card, email, name, postcode, at payment | see what was read |
| The vault host (SG/Send) | ciphertext, sizes, times | read a share, a note or an account; the keys are the reader's and the newsroom's |
| The newsroom's agent, with the lane's read key | shares, subscriptions and notes, decrypted | write to the lane or to a reader's vault |
What leaves the browser
Nine channels, each listed with what, when and who sees it, on state, inputs and outputs, read from the map. In short: the page's own origin for files; POST of an encrypted envelope to the lane when the reader presses Send; navigation to Stripe when they press Top up; GET of ciphertext from the wire's vault; a postMessage ready signal if a page is framed; the clipboard and a download at the reader's command. The tests record the hosts a page contacted and fail on any other.
The accepted gaps, written down
The security model on the newsroom (meter/security.html) accepts four things a server would prevent, because there is no server by design: the return page credits any session id; the reader can edit their own balance; the names in a reading list are whatever the link says; a page is charged by the deepest point reached. The review's job was to prove those are the only gaps.
The review: 18 findings, each a public issue
Before the code moved here it was mapped and reviewed line by line, the way a fractal semantic graph is read: every file, every control a reader can click, type or pay, every input and output, every contract, then every function the findings name. The findings are issues 1 to 18 in the repository, labelled by phase, area and severity. Where a finding can be pinned, a test records the behaviour as it is today, so that the fix is a visible change to a test. The three a security evaluator will want first:
- No Content-Security-Policy on any page (R10, high). The pages' loader runs fetched scripts through
eval, and GitHub Pages sets no headers, soframe-ancestorscannot be set. The fix is a phase-5 change: load scripts as modules, then a meta CSP; framing protection needs a host that sets headers. - Payment references unbounded and unchecked (R14, low) and restore() trusts a file's fields (R2, low): the exposure is a file someone sent the reader to restore.
- Production reader data goes to hosts named dev (R5): a naming decision for the operator, listed so that nobody is surprised.
The full table, with the test that pins each and the proposed change.
Supply chain
- Third-party scripts on the reader pages: none. The build fails if one appears (the brief's rule).
- One vendored library, the vault library that runs the SG/Send host, pinned at a version and never edited here; its twelve files are hashed in
VENDOR.mdand the test checks them. - Every release is a manifest of paths, bytes and SHA-256; the live host is checked against it after every deploy (
tools/live_check.py), because green does not mean live. - Nothing is edited in place. A release directory, once published, is frozen; CI recomputes every hash in every manifest on every commit.
The proof of the move
This code lived in the newsroom's repository until 13 October 2026. Moving it to a host of its own was done as a pure refactoring, and proved: of 74 files, 60 hash exactly as they did before, and the three that differ do so by the lines that located the site root from the script's own address, each recorded in the manifest with its before-hash and the reason; of 597 pages on the newsroom, 593 normalise to what they were. The proof, with the report it was written from.
The pipeline
Every commit: syntax, the manifest, a build that is the same bytes twice, and the tests in Chromium, WebKit and Firefox against the newsroom's real pages, in about four minutes. Every push to main: a tag, the Pages deploy, and the live check. A pull request needs a person and an agent reviewer. The runs.
Verify it yourself
# every file of the current release, with its hash, from the live host
python3 - <<'END'
import json, hashlib, urllib.request
host = "https://api.newsroom.sgit.ai/v0.2.5/"
m = json.load(urllib.request.urlopen(host + "manifest.json"))
for f in m["files"]:
b = urllib.request.urlopen(host + f["path"]).read()
print("ok " if hashlib.sha256(b).hexdigest() == f["sha256"] else "BAD", f["path"])
END
# the same, from the repository's own tool
git clone https://github.com/SGit-AI/SGit-AI__Website__Newsroom__API && cd SGit-AI__Website__Newsroom__API && python3 tools/live_check.py
The newsroom's gate does the equivalent on its side: every page must load the reader code from the pinned release, by a URL that is in the manifest, and never by a relative path. Its source is public too.
Licence and provenance
Apache 2.0. The code was written by the newsroom's agents under Dinis Cruz's editorial and engineering responsibility, as every page on newsroom.sgit.ai discloses; the review was done by agents and is open to people, which is what the issues are for. About, licence, contact.