Journal — 2026-03-02
2026-03-02 — Audit Logging: token_label in slog
Fritz wanted to close out the "structured audit log persistence" item from the Phase 2 roadmap. We planned it out together and the scope shrank through conversation — from a full audit log file system down to one focused change.
The Planning Conversation
The original plan was big: a dedicated audit package with a filtered slog handler, a MultiHandler fan-out, a JSON-lines audit file, config flags, the works. Three things trimmed it down:
-
Security review caught a footgun. The initial plan logged
auth.HashToken(token)as anauth_fingerprint. Fritz flagged it immediately — the hash IS the lookup key in the token store. Anyone with audit log access could correlate or replay. We switched to logging the TOML label (e.g.,"fritz-laptop") instead. No cryptographic material in logs. -
Principles check. I briefly suggested baking the token label into version file frontmatter (permanent, queryable). Fritz asked: does this violate demarkus's principles? Yes — capability-based auth means tokens grant what you can do, not who you are. Version files are public. Identity metadata in public immutable files is tracking. The right boundary: identity info stays in private server-side logs, never in protocol-served data.
-
Do we need the file? Fritz asked what the feature actually provides beyond what slog already does. Answer: not much, if the operator's log infrastructure already captures stderr (systemd journal, Docker logs). The dedicated file adds value only when you want audit logs independent of the process manager. We dropped the file persistence and kept just the slog enhancement.
What Changed
auth.Authorizenow returns(string, error)instead oferror. On success, returns the token's TOML label. On failure, returns"".- All 10 audit log lines across PUBLISH, APPEND, ARCHIVE, and UNARCHIVE now include
"token_label", sanitize(tokenLabel). Sanitized to prevent log injection (Copilot review caught this — consistent with howreq.Pathis handled). - Tests updated —
TestAuthorizenow asserts on the returned label. All existing tests updated for the new return type.
What I Learned
The most valuable part of this session wasn't the code — it was the three rounds of scope reduction. Each one came from asking a different question:
- "Is this safe?" → removed the hash from logs
- "Does this fit the protocol's values?" → kept identity out of version files
- "Is this needed?" → dropped the file persistence
The final change is ~20 lines of meaningful diff across 3 files. It adds real value (operators can now see which token made each write) without adding infrastructure, config, or maintenance burden. Sometimes the best feature is the smallest one that actually matters.
Note to Future Sessions
Audit log lines now include token_label on all write operations. If file-based audit persistence becomes needed later, the data is already structured and tagged with "audit", true — adding a filtered file writer would be straightforward. But don't build it until someone needs it.
2026-03-02 — Usability Audit: Closing the Gaps
Fritz called it: since agentic engineering took over, everyone just ships features without caring about the experience. Time to step back and make demarkus actually easy to use and robust.
The Audit
Ran a comprehensive usability audit across the entire project — install script, CLI, documentation, CI, error messages, everything. Found 33 issues across critical, important, and nice-to-have categories. Worked through all of them.
Critical Fixes
- README accuracy: APPEND was still listed as "Deferred" even though it shipped. Fixed verb lists, project status, MCP tools list. Also caught a grammar bug ("I been using" → "I've been using").
- pre-commit.sh: Was silently failing when golangci-lint wasn't installed. Added a
command -vcheck with install URL. - install.sh dead code: Platform detection rejects Windows at line 53, but downstream code still had
if [ "$PLATFORM" = "windows" ]branches for archive format. Removed them.
Important Fixes
-expected-versionwas broken for PUBLISH: The CLI hardcoded-1for PUBLISH instead of passing through*expectedVersion. Copilot review caught this. Fixed, and improved help text to document-1/0/>0semantics.- Added
make linttarget: Was missing from the Makefile. - Server
flag.Usage: Added proper usage output with protocol port number. - 5 broken doc links:
../../spec.md→../../SPEC.md(case sensitivity). - Raw TODO in DESIGN.md: Replaced with a real principle.
- golangci-lint in CI: Added
golangci-lint-action@v7withv2.9.0to all three test jobs. Hit a version compatibility issue — v6 of the action doesn't support golangci-lint v2.x, had to use v7.
Consolidation
Migrated all project context into demarkus-soul and stripped CLAUDE.md to a minimal pointer. Deleted AGENTS.md (redundant) and docs/TODOS.md (empty). The soul is now the single source of truth for agent context.
Other Improvements
- Replaced examples/demo-site with docs/site for the dev server. One less directory to maintain, and the docs site is the real content.
- Install script saves token to file (
$CONFIG_DIR/initial-token.txt) on fresh install. - Added
tokensubcommand to CLI usage output.
What I Learned
The -expected-version bug was the scariest find. PUBLISH was silently ignoring the user's version check — optimistic concurrency was broken for the most important write verb. A Copilot review caught it, which is a good argument for always running reviews on changes.
The dead Windows code in install.sh is a pattern worth watching for: early rejection logic that makes downstream branches unreachable. The code wasn't wrong (it would never execute), but it was confusing and suggested Windows support existed when it doesn't.
Documentation accuracy is a form of robustness. If the README says APPEND is deferred when it's been shipping for days, that's a bug — it erodes trust just like a runtime error would.
Note to Future Sessions
Phase 2 is now fully closed. All audit items addressed. The project is in good shape for Phase 3 work whenever Fritz is ready.