# Publishing Without a Platform **A zero-database static index on Cloudflare Workers and R2** Point Arena Cyber Security Version 1.0 --- ## Summary This paper documents the architecture running at `open.pointarenacybersecurity.dev`, the index you are probably reading this from. It is roughly 400 lines of JavaScript and an object storage bucket. There is no database, no authentication, no write path reachable from the internet, and no user input that reaches a parser. We are publishing it because the architecture is the argument. A small organization that needs to distribute files publicly does not need a platform, a CMS, or a managed service. It needs about an afternoon. The paper covers the threat model, what the design removes, what risk remains after the removals, and how to reproduce it. It includes a vulnerability we found in our own implementation while writing the threat model, because the process of finding it is more instructive than the fix. --- ## 1. Why this exists The tooling this organization builds on has always been free. Nmap, Suricata, Wazuh, nuclei, MISP. What is not free is the knowledge of how to run them well. That sits behind certification costs, training paywalls, consulting retainers, and vendor-managed platforms. A small nonprofit could technically run a SIEM for nothing and never will, because nobody has written down the path from zero to working in language they can follow. That gap is the paywall. Publishing is therefore the primary activity, not a side effect of it. Which makes the publishing mechanism itself worth getting right, and worth explaining. --- ## 2. Threat model Most security architecture writing starts from confidentiality and works outward. That is the wrong starting point here, and getting the starting point right is most of the design. **Everything this system serves is intended to be public.** There is no secret to protect. Confidentiality is not a property we are defending, it is a property we have deliberately given up. What remains: | Property | Defending? | Why | |---|---|---| | Confidentiality | No | All content is published deliberately | | Integrity | **Yes, primary** | Readers run these scripts as root | | Availability | Yes, secondary | AGPL section 13 obligates source availability | | Attribution | Yes | License terms depend on provenance being clear | Integrity is the one that matters. We publish hardening scripts and detection content. Somebody is going to `curl | sudo bash` one of these against their advice and ours. If an attacker can alter what we serve, they get root on a nonprofit's infrastructure with our name on the payload. Availability matters for a less obvious reason. This work is AGPL-3.0-or-later. Section 13 requires that anyone running a modified version over a network offer the corresponding source to that service's users. We run a modified version over a network. The source has to stay reachable for as long as the service does, so availability is a license obligation, not just an operational preference. ### Adversaries considered 1. **Opportunistic scanners.** Automated, high volume, looking for known CMS and framework vulnerabilities. The overwhelming majority of traffic. 2. **A targeted attacker seeking content substitution.** Wants to alter a published script so downstream readers execute attacker-controlled code. 3. **A resource-exhaustion attacker.** Wants to drive up cost or take the site down, with no interest in the content. 4. **The platform itself.** Cloudflare can read and alter anything here. This is accepted, and section 7 explains why and what it costs. --- ## 3. Architecture ``` reader | | HTTPS v Cloudflare edge (TLS, DDoS, cache) | v Worker (~400 lines, no state) | | binding, not a network call v R2 bucket (private, no public URL) ``` The Worker does four things: - **Listing.** Calls `list()` on the bucket, groups keys by prefix, renders a directory table. Cached 60 seconds in the isolate. - **View.** Fetches one object, computes its SHA-256, renders it as escaped text with its metadata. - **Raw.** Streams one object through with a corrected content type. - **Digests.** Generates `SHA256SUMS` across the whole bucket on demand. There is no build step and no deployment pipeline for content. Publishing a file is uploading it to the bucket. The listing is derived from the bucket at request time, so the index cannot drift from what is actually being served. This is a deliberate choice over a generated manifest, which can and does go stale. License assignment is by path prefix. Anything under `papers/` is CC BY-SA 4.0, everything else is AGPL-3.0-or-later. Encoding policy in structure rather than metadata means it cannot be forgotten on a per-file basis. --- ## 4. What is absent The security properties here come almost entirely from what is not present. **No database.** There is nothing to inject into. The entire class of SQL and NoSQL injection is not mitigated, it is inapplicable. **No authentication.** No login form, no session handling, no password storage, no reset flow, no token issuance. Credential stuffing, session fixation, and privilege escalation have nothing to attach to. This is a direct consequence of the threat model: because we gave up confidentiality, we do not need access control, and access control is where a large share of web vulnerabilities live. **No write path on the internet.** Uploads happen through the storage provider's own console, authenticated separately. No endpoint in the Worker accepts a `POST`. Non-`GET` methods return 405 before any other logic runs. **No parsing of untrusted input.** The request path is used for two things: a literal `Map` lookup against keys already read from the bucket, and a string prefix comparison for directory grouping. No template engine, no deserializer, no markdown renderer, no YAML loader. File contents are HTML-escaped and placed inside `
`. A published file containing a `