Add HackMD API token secret - #59
Open
alycda wants to merge 10 commits into
Open
Conversation
Declare a new agenix secret for the HackMD API token, following the existing personal/ secret pattern (git-config, agent-instructions): - secrets/secrets.nix: declare personal/hackmd-api-token.age with the personal age recipient key - secrets/personal/hackmd-api-token.age: PLACEHOLDER file, not real ciphertext - to be replaced with the armored age encryption of the token before switching (rage -e -a -r <key>, or agenix -e) - home-manager/modules/tools/hackmd.nix: new tool module that wires the secret; decrypts to ~/.local/share/agenix/hackmd-api-token via the secretsDir/identityPaths already configured in modules/git.nix - home-manager/profiles/home.nix: import the module in the personal profile (the token is personal-keyed; other profiles can opt in later) Consumers should read config.age.secrets.hackmd-api-token.path at use time rather than baking the token into the environment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G4o4LduK88LUqj8kb2Z4LU
alycda
commented
Jul 30, 2026
alycda
commented
Jul 30, 2026
- hackmd.nix: `_:` -> `{ config, ... }:` (was referencing undefined `config`)
and add the missing closing brace; the module didn't parse.
- Point `file` at secrets/personal/ and `git mv` the .age there, matching
secrets.nix (which declares personal/) and the existing personal/ pattern.
Previously secrets.nix declared personal/ while the file + module used work/,
so agenix could never produce ciphertext at the path the module reads.
- Document that the decrypted content must be env-file format
(`HACKMD_API_TOKEN=<token>`) at ~/.config/hackmd/env, so the Claude Code
hackmd MCP launcher's `. ~/.config/hackmd/env` sources it correctly.
- Fix stale comments claiming ~/.local/share/agenix/hackmd-api-token and the
wrong HMD_API_ACCESS_TOKEN var name.
Placeholder ciphertext still needs replacing with real ciphertext before
`home-manager switch`:
agenix -e secrets/personal/hackmd-api-token.age # type: HACKMD_API_TOKEN=<token>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The token is a work account key, and this repo separates secrets by profile (single age recipient; personal/ vs work/ is organizational). The work Mac (darwinConfigurations."ditto", host DTO-A311-evans) and work devcontainer (alyssa@work-dev) both run the work home profile; the personal laptop (alyssa@home) runs home.nix. - Move the secret to secrets/work/hackmd-api-token.age and declare it there in secrets.nix (was personal/). - Import the module from work.nix (this machine) instead of home.nix, so the work token deploys where the hackmd MCP actually runs. Net vs main: hackmd is added only to the work profile. Real ciphertext still required before switch: agenix -e secrets/work/hackmd-api-token.age # type: HACKMD_API_TOKEN=<token> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Some machines use a personal HackMD account, others the work account. Wire a symmetric parallel so each profile mounts its own account's key to the same ~/.config/hackmd/env path the MCP launcher sources. - Rename hackmd.nix -> hackmd-work.nix (work account, imported by work.nix). - Add hackmd-personal.nix (personal account, imported by home.nix). - Declare secrets/personal/hackmd-api-token.age alongside work/ in secrets.nix (placeholder ciphertext, replace via agenix -e before switch). Both modules define age.secrets.hackmd-api-token at the same decrypt path, so they are mutually exclusive — exactly one per profile (work.nix XOR home.nix). Fill in the personal token with: agenix -e secrets/personal/hackmd-api-token.age # type: HACKMD_API_TOKEN=<token> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The personal HackMD key is now the actual credential rather than the shared placeholder ciphertext both accounts started from. Verified it decrypts under the personal identity and is in the env-file format the launcher sources (HACKMD_API_TOKEN=...). The work blob is deliberately untouched — it still carries the original placeholder and needs the same treatment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds yuna0x0/hackmd-mcp as a module imported by the two account modules, so the server and the token it needs always arrive together and pick up whichever account the profile decrypted. The token never reaches the repo or ~/.claude.json: the launcher sources ~/.config/hackmd/env at spawn time. An `env` block in the MCP entry would put the credential in world-readable plaintext, which is the thing agenix exists to prevent. Why an activation script rather than home.file: Claude Code has no declarative user-scope MCP config. settings.json has no mcpServers key and .mcp.json is project-scoped, so a user-scope server must live in ~/.claude.json — a file Claude Code rewrites constantly. Symlinking a read-only store path over it would break the app, so we converge the one key we own with jq, the same idempotent approach claude-code.nix already uses for the CLAUDE.md import line. Version is pinned: bare `npx -y hackmd-mcp` resolves to whatever is newest at spawn time, so the tool surface could change between two sessions on an unchanged flake — the opposite of what pinning nixpkgs buys. Verified end-to-end: the launcher answers an MCP initialize handshake as hackmd-mcp 1.5.7 with the token sourced from the agenix env file, and fails with a clear message when the secret is absent. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`agenix -e` failed on every secret with "Tried to reset after the underlying buffer was exceeded", pointing at src/age.rs — so the obvious read was a corrupt .age file. It wasn't. The error string lives in the age crate's identity-file parser, and the real cause was one missing byte at the end of ~/.age/personal-key.txt. Two dead ends are recorded because both are reasonable and both are wrong: the corrupt-ciphertext theory (killed by rage decrypting the same file fine), and pinning ragenix to a known-good version — there isn't one. ragenix vendors age 0.10.1 at HEAD and the fix landed in age 0.11.0, so it needs a dependency bump, not an input bump. Session history supplied the provenance the current session couldn't: the key was pasted from a password manager rather than generated by age-keygen, which is exactly the step that omits the trailing newline, and the container injection path is byte-exact — so the durable fix is the host copy, not this container. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An agent reading CLAUDE.md had no way to learn the knowledge store exists; `rg docs/solutions` over the repo returned nothing. The store only compounds value if it gets found, and fresh sessions and collaborators without the plugin are exactly who need the pointer. One line in the existing structure tree rather than a new section — the smallest change that names the folder, its organisation, and the frontmatter fields worth searching on. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four conflicts, all from the branch being 20 commits behind: - secrets/secrets.nix — purely additive on both sides. Kept both blocks and reworded the HackMD comment to point at the Linear convention above it, since both now use directory-carries-the-identity. - home.nix / work.nix — main moved agent-skills.nix into common.nix, so its import was dropped from both profiles and only the hackmd-* import was kept. Keeping it would have duplicated what common.nix already does. - CLAUDE.md — took main's side wholesale. It already documents docs/solutions/, and more thoroughly than the line added on this branch (it also covers CONCEPTS.md and describes when the store is relevant), so that commit is now a no-op. Its structure block also corrects the Dockerfile/docker entries for multi-arch. Verified after resolving: statix, deadnix, and `nix flake check --all-systems` all pass, and both the work and home profiles evaluate with the hackmd modules imported. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
⊕ Entity-level changesdocs/solutions/runtime-errors/ragenix-edit-fails-on-identity-without-trailing-newline.md
home-manager/modules/tools/hackmd-mcp.nix
home-manager/modules/tools/hackmd-personal.nix
home-manager/modules/tools/hackmd-work.nix
home-manager/profiles/home.nix
home-manager/profiles/work.nix
secrets/personal/hackmd-api-token.age
secrets/secrets.nix
secrets/work/hackmd-api-token.age
Summary: 33 added, 2 modified across 9 files (10 added orphans) functions and classes, not lines · sem |
alycda
marked this pull request as ready for review
August 5, 2026 10:24
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scaffolds a new agenix secret for the HackMD API token, following the existing
personal/secret pattern (git-config,agent-instructions).Changes
secrets/secrets.nix— declarepersonal/hackmd-api-token.agewith the personal age recipient keysecrets/personal/hackmd-api-token.age—agenix -e secrets/personal/hackmd-api-token.age)home-manager/modules/tools/hackmd.nix— new tool module wiring the secret; decrypts to~/.local/share/agenix/hackmd-api-tokenvia thesecretsDir/identityPathsalready configured inmodules/git.nixhome-manager/profiles/home.nix— import the module in the personal profile only. Other profiles can opt in with the same one-line import if the token is wanted there.Notes
home-manager switchon a profile importing this module will fail at decryption until the placeholder is replaced with real ciphertext.config.age.secrets.hackmd-api-token.pathat use time (e.g.export HMD_API_ACCESS_TOKEN="$(cat ~/.local/share/agenix/hackmd-api-token)") rather than baking the token into the environment.🤖 Generated with Claude Code
https://claude.ai/code/session_01G4o4LduK88LUqj8kb2Z4LU
Generated by Claude Code