diff --git a/documentation/backlog.md b/documentation/backlog.md
index 865c9f2..4e6af84 100644
--- a/documentation/backlog.md
+++ b/documentation/backlog.md
@@ -8,12 +8,6 @@ Labels: `[feature]` `[infra]` `[security]` `[ux]` `[debt]`
Things that are actively in progress or should be picked up immediately. Mostly operational risk and the remaining phase 7 hardening work.
-- **Pin dependencies in package.json** `[debt]` `[infra]`
- Unpinned deps in a CI/CD pipeline are a real risk. Pin all versions to exact values to prevent unexpected breakage on build.
-
-- **Docker credential helper** `[debt]` `[infra]`
- Credentials are stored unencrypted in `~/.docker/config.json`. Set up a credential helper. See https://docs.docker.com/go/credential-store/
-
- **Google OAuth publishing** `[infra]`
Only test users can currently log in via Google. Publish the OAuth consent screen so any Google user can sign in — requires branding verification in Google Cloud Console.
@@ -108,6 +102,8 @@ Directionally right, timing is unclear. Revisit when the next/now work is done.
Shipped milestones, newest first.
+- **04 - 2026 - t00001 - Docker credential helper**
+- **04 - 2026 - Pin dependencies in package.json** - Unpinned deps in a CI/CD pipeline are a real risk.
- **04 - 2026 - React error boundaries** - Catch and display runtime errors gracefully instead of crashing the entire app.
- **04 - 2026 - 404 and redirect handling** - Unknown routes return raw errors. Add a catch-all route on the frontend for client-side 404s.
- **04 - 2026 - Multiplayer GameService unit tests** - round evaluation, scoring, tie-breaking, timeout handling
diff --git a/documentation/notes.md b/documentation/notes.md
index eb25e7a..8a8d414 100644
--- a/documentation/notes.md
+++ b/documentation/notes.md
@@ -13,6 +13,7 @@ task description.
4. if we go through a file, we'll do it slowly section by section, no matter how many sections
5. how to name the current feature branch? also tell me when its time to git commit and provide a commit message
6. if we have multiple options to do something, also always provide options that reflect current industry standards and best practices
+7. For every completed task, produce a ticket file in documentation/tickets/. Use ADR format (adr-) for decisions between options with long-term consequences. Use feat-/fix-/chore- for routine tasks. Always include a setup guide or summary of what was done. Suggest the filename.
## tasks
diff --git a/documentation/tickets/blueprint.md b/documentation/tickets/blueprint.md
new file mode 100644
index 0000000..7e612bb
--- /dev/null
+++ b/documentation/tickets/blueprint.md
@@ -0,0 +1,77 @@
+# Ticket Blueprint
+
+Two formats depending on task type. Choose based on whether a meaningful
+decision between options was made.
+
+---
+
+## Format A — ADR (architectural/infrastructural decisions)
+Use when: you chose between options with long-term consequences.
+Prefix: `adr-`
+
+---
+
+# ADR:
+
+## Status
+Accepted | Superseded by | Deprecated
+
+## Date
+YYYY-MM-DD
+
+## Context
+What is the problem? Why does it need to be solved?
+
+## Decision
+What was chosen and why in one or two sentences.
+
+## Options considered
+
+### Option A — ✅
+Description. Why it was chosen.
+
+### Option B —
+Description. Why it was rejected.
+
+## Consequences
+- What gets better
+- What gets worse or more complex
+- Operational implications
+- What breaks if this needs to be redone
+
+## Affected files / machines
+- List files, servers, or systems touched
+
+## References
+- Links to relevant docs
+
+---
+
+## Setup guide / implementation notes
+Step-by-step of what was actually done.
+
+---
+
+## Format B — Task (features, fixes, chores)
+Use when: routine task with a clear solution.
+Prefix: `feat-` / `fix-` / `chore-`
+
+---
+
+# :
+
+## Problem
+What was wrong or missing?
+
+## Options considered
+### Option A — ✅
+### Option B —
+
+## Solution
+What was done and why.
+
+## Files changed
+- `path/to/file.ts`
+
+## Commit
+`: `
diff --git a/documentation/tickets/t00001.md b/documentation/tickets/t00001.md
new file mode 100644
index 0000000..d15f242
--- /dev/null
+++ b/documentation/tickets/t00001.md
@@ -0,0 +1,95 @@
+# ADR: Docker Credential Helper Setup
+
+## Status
+Accepted
+
+## Date
+2026-04-26
+
+## Context
+Docker credentials for `git.lilastudy.com` and `dhi.io` were stored as
+base64-encoded strings in `~/.docker/config.json` on both the dev laptop
+and the VPS. Base64 is not encryption — anyone with read access to the
+file can decode the credentials instantly.
+
+## Decision
+Use `pass` (GPG-backed password store) as the Docker credential helper
+on both machines.
+
+## Options considered
+
+### Option A — `pass` (GPG-backed) ✅
+Stores credentials encrypted with a GPG key. Works on headless servers
+and desktops without GNOME. Industry standard for Linux servers.
+
+### Option B — `secretservice` (GNOME keyring)
+Uses the desktop keyring daemon. Not suitable for a headless VPS, and
+not suitable for an i3 desktop without running `gnome-keyring-daemon`
+manually.
+
+### Option C — `gnome-libsecret`
+Same limitations as Option B.
+
+## Consequences
+- Credentials are now GPG-encrypted at rest on both machines
+- Requires GPG passphrase entry when Docker needs to pull credentials
+ in a new session
+- Must be set up manually on each machine — not reproducible via the repo
+- VPS setup must be repeated if the server is reprovisioned
+
+## Affected machines
+- Dev laptop (Debian 13, i3)
+- VPS (Debian 13, ARM64, headless)
+
+## References
+- https://docs.docker.com/reference/cli/docker/login/#credential-stores
+- https://www.passwordstore.org/
+
+---
+
+## Setup guide
+
+Repeat these steps on each machine.
+
+### 1. Install dependencies
+```bash
+sudo apt-get install -y pass gnupg2 golang-docker-credential-helpers
+```
+
+### 2. Generate a GPG key
+```bash
+gpg --full-generate-key
+```
+Choose RSA, 4096 bits, no expiry. Set a strong passphrase.
+
+### 3. Get the key ID
+```bash
+gpg --list-secret-keys --keyid-format LONG
+```
+Copy the hex string after the `/` on the `sec` line.
+
+### 4. Initialise pass
+```bash
+pass init
+```
+
+### 5. Update `~/.docker/config.json`
+Replace the entire file contents with:
+```json
+{
+ "credsStore": "pass"
+}
+```
+
+### 6. Re-login to registries
+```bash
+docker login git.lilastudy.com
+# dev laptop only:
+docker login dhi.io
+```
+
+### 7. Verify
+```bash
+cat ~/.docker/config.json
+```
+Should show only `"credsStore": "pass"` with no `auths` block.