blumeops/docs/zk/1768246525-RVRY.md
Erich Blume b8104d75ad Move zk cards to docs/zk/ for documentation restructuring (#84)
## Summary
- Move all existing zettelkasten cards from `docs/` to `docs/zk/` as a temporary holding area
- Update `zk-docs` mise task to look in the new location
- Add `docs/README.md` explaining the Diataxis-based restructuring plan and target audiences

## Context
This is phase 1 of a multi-phase documentation restructuring effort. The goal is to reorganize docs to follow the Diataxis framework while serving multiple audiences:
1. Erich (owner) - knowledge graph/zk
2. Claude/AI agents - memory and context enrichment
3. New external readers - high-level overview
4. Potential operators/contributors - onboarding
5. Replicators - people wanting to duplicate the approach

## Testing
- [x] Verified `mise run zk-docs` still works with the new path
- [x] Updated obsidian.nvim config (in ~/.config/nvim) to point to new path

## Note
The obsidian.nvim config change is outside this repo but was made as part of this work.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/84
2026-02-03 09:13:50 -08:00

5.2 KiB
Raw Blame History

id aliases tags
1768246525-RVRY
forgejo
forge
blumeops
forgejo
git
scm
forge

Mon Jan 12 11:35

 brew install forgejo
 brew --prefix forgejo
/opt/homebrew/opt/forgejo
 brew services start forgejo
==> Successfully started `forgejo` (label: homebrew.mxcl.forgejo)

From the service definition I can see that this runs as:

/opt/homebrew/opt/forgejo/bin/forgejo web --work-path /opt/homebrew/var/forgejo > /opt/homebrew/var/log/forgejo.log 2> /opt/homebrew/var/log/forgejo.log

It sounds from the docs like this means the config file should live at:

/opt/homebrew/var/forgejo/custom/conf/app.ini

Ah, based on the logs, it looks like forgejo has picked port 3000 which is used by grafana:

 lsof -nP -iTCP:3000 -sTCP:LISTEN
COMMAND  PID       USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
grafana 1530 erichblume   15u  IPv6 0x4acfad8b21dcb063      0t0  TCP *:3000 (LISTEN)

Ok I've set a basic config for port 3001, and then gone through the basic app setup. Looks like it's working! Not sure how SSH works yet though. Let's get this service registered.

Ok so the next issue is that I want to use ssh as my primary git interface, and I want that to look to users like I'm using port 22 but I want to host it on indri which has its own separate ssh setup. Hmm. Let's tell forgejo to use port 2200. Ah perfect, we can set SSH_PORT to 22 and SSH_LISTEN_PORT to 2200.

Hmm, let's stop running this as me and run as a new user, 'forgejo'.

sudo sysadminctl -addUser forgejo -system -shell /usr/bin/false
sudo chown -R forgejo:staff /opt/homebrew/var/forgejo

Ok, I think I need to switch all my services on this host over to a services file.

Wow, missing from the above is like 4 hours of deep diving in to the particulars of tailscale service definition hosting. In the end, I never got a services file to work - and yes, I did remember to advertise! Adding to the complexity is that I didn't discover until the end that you can't do "hairpinning", ie you CANNOT use the tailnet service name from the host doing that hosting. I probably had it fixed at some point hours ago and ruled it out because I didn't know about the hairpinning issue. So anyway... what ended up working was to just use the cli:

tailscale serve --service="svc:forge" --tcp=22 tcp://localhost:2200
tailscale serve --service="svc:forge" --https=443 http://localhost:3001

That's it. Nothing else needed, worked right away. Sheesh. (Ok there was also a solid hour spent on permission issues... I honestly don't know how it's working now, as there is now a forgejo user and the config says to use it but the files are all owned by erichblume:staff but with group permissions set... in any case, it friggin' works. So I'm happy.

Configuration (Ansible-Managed)

As of 2026-01-23, the app.ini is managed by ansible:

  • Template: ansible/roles/forgejo/templates/app.ini.j2
  • Secrets fetched from 1Password in playbook pre_tasks
  • Secrets item: "Forgejo Secrets" in blumeops vault (fields: lfs-jwt-secret, internal-token, oauth2-jwt-secret, runner_reg)

Deploy config changes:

mise run provision-indri -- --tags forgejo

Forgejo Actions (CI/CD)

Runner (k8s)

The Forgejo runner runs in Kubernetes with Docker-in-Docker (DinD) for container builds.

Architecture:

  • Runner daemon + DinD sidecar in a single pod
  • Jobs execute in containers using the k8s label
  • DinD exposes Docker API on tcp://127.0.0.1:2375
  • Pods reach *.ops.eblu.me services via Caddy reverse proxy

Components:

  • ArgoCD app: argocd/apps/forgejo-runner.yaml
  • Manifests: argocd/manifests/forgejo-runner/
  • Job image: registry.ops.eblu.me/blumeops/forgejo-runner (Node.js + Docker CLI)
  • Job image source: containers/forgejo-runner/

Deployment:

# Apply secret (contains runner token from 1Password)
op inject -i argocd/manifests/forgejo-runner/secret.yaml.tpl | kubectl --context=minikube-indri apply -f -

# Sync via ArgoCD
argocd app sync forgejo-runner

View logs:

kubectl --context=minikube-indri logs -n forgejo-runner -l app=forgejo-runner -c runner

Container Build Workflow

Container images are built via .forgejo/workflows/build-container.yaml, triggered by tags matching <container>-v<version>.

Release a container:

mise run container-list                            # See available containers
mise run container-tag-and-release nettest v1.0.0  # Tag and trigger build

Test container (containers/nettest/): Network connectivity test for debugging CI/CD.

Workflows

Workflows live in .forgejo/workflows/ (not .github/workflows/).

Important: Use github.* context variables, not gitea.*. Forgejo supports both at runtime, but:

  1. The Forgejo web UI schema validator only recognizes github.*
  2. actionlint pre-commit hook validates workflows locally (catches errors before push)
  3. Pass untrusted inputs (like github.head_ref) through env vars for security

Runner Token

Stored in 1Password "Forgejo Secrets" item, field runner_reg.

To create a new token:

  1. Go to https://forge.ops.eblu.me/admin/actions/runners
  2. Click "Create new Runner"
  3. Copy the token and update 1Password