2.2 KiB
Security —
Status: Current
Owner: <who maintains this>
Last reviewed: <YYYY-MM-DD>
Governs: authentication, secret handling, data at rest and in transit
Review trigger: Any new secret, any new external service, any change to auth or storage
What this protects, and from whom
<Name the assets and the adversary. "Security" with no threat model produces effort spent in the wrong places — a hobby project's real adversaries are a leaked repository and a stolen laptop, not a nation state, and the controls differ.>
| Asset | Where it lives | What would it cost to lose |
|---|---|---|
| <> | <> | <> |
Secrets
- Nothing secret is committed. Not in source, not in config, not in a test fixture, not in a screenshot.
- Secrets arrive from the environment or a secret store, and are read at the boundary rather than passed around.
- A key stored beside the data it protects protects nothing. If data is encrypted at rest, say here where the key lives and how it is rotated.
Check before every release: no private keys, keystores, .env files,
service-account JSON, or real credentials in the tracked tree — including in the
history, which a git log -p search covers and a directory listing does not.
Authentication and authorisation
<Who can do what, and where that is enforced. If the answer is "in the UI", it is not enforced — say so and fix it.>
Data in transit
<TLS everywhere it applies. Certificate pinning if it applies. What is sent to third parties, and what is deliberately not.>
Third parties
| Service | What it receives | Why that is acceptable |
|---|---|---|
| <> | <> | <> |
Every row is a decision to send someone else's data somewhere. An empty table is a good table.
Deliberately out of scope
<What is not defended against, and why that is a reasonable call. Writing it down converts an unknown gap into a known one, which is the whole difference.>
Logging
Logs are read by people and shipped to places. Nothing sensitive goes into one — not a token, not a credential in a URL, not the contents of a user's record. An error's name is almost always enough; its message, from a failed HTTP request, is often the request URL.