From 373e0c4be8cd446020336e2b793ec289fe98e12c Mon Sep 17 00:00:00 2001 From: Charlie Root Date: Tue, 31 Mar 2026 16:12:45 +0200 Subject: [PATCH] docs: add Gitea store, dispatch, and server push technique from blog CLAUDE.md --- CLAUDE.md | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0fb375ac..587fad94 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -195,5 +195,81 @@ npm install git+https://gitea.giersig.eu/svemagie/indiekit-endpoint-activitypub | `INDIEKIT_BIND_HOST` | Jail IP for webmention poller direct connect | | `REDIS_URL` | Redis for AP message queue + KV (production; without this, queue lost on restart) | | `MONGO_HOST` / `MONGO_URL` | MongoDB connection | -| `GH_CONTENT_TOKEN` | GitHub token for writing posts to the `blog` repo | +| `GH_CONTENT_TOKEN` | Gitea PAT for writing posts to the indiekit-blog repo | | `SECRET` | JWT signing secret (webmention poller auth) | + +--- + +## Content store (Gitea) + +`@indiekit/store-github` is pointed at the self-hosted Gitea instance instead of GitHub. Key config in `indiekit.config.mjs`: + +```js +"@indiekit/store-github": { + baseUrl: giteaBaseUrl, // GITEA_BASE_URL from .env + user: process.env.GITEA_CONTENT_USER, + repo: process.env.GITEA_CONTENT_REPO, + branch: "main", + token: githubContentToken, // GH_CONTENT_TOKEN from .env +} +``` + +**`GITEA_BASE_URL`** must end with a trailing slash: `http://10.100.0.90:3000/api/v1/` +Without it, `new URL(apiPath, baseUrl)` silently strips the `v1` segment → 404 on all writes. + +**`GH_CONTENT_TOKEN`** — the Gitea PAT for `svemagie`. `start.sh` rejects startup if neither `GH_CONTENT_TOKEN` nor `GITHUB_TOKEN` is present. The token must have repo read/write scope on `giersig.eu/indiekit-blog`. + +**`GITEA_CONTENT_USER`** = `giersig.eu` (the org, not the personal username) +**`GITEA_CONTENT_REPO`** = `indiekit-blog` + +--- + +## Micropub → Gitea build dispatch + +Gitea Contents API commits (what `store-github` does) do **not** trigger `on: push` CI workflows. `patch-micropub-gitea-dispatch.mjs` patches the Micropub endpoint to fire a `workflow_dispatch` event to `giersig.eu/indiekit-blog` after each create/update, so the blog rebuilds immediately after a post is published. + +--- + +## Pushing changes from the server + +The node jail shell is tcsh, which mangles multi-line `echo`/`printf` and inline heredocs. To push file changes to Gitea from the server, use a Python script: + +```python +python3 << 'PYEOF' +import urllib.request, json, base64 + +TOKEN = "your-gitea-pat" +REPO = "giersig.eu/indiekit-blog" +PATH = ".github/workflows/deploy.yml" +BASE = "http://10.100.0.90:3000/api/v1" + +# 1. Get current SHA +req = urllib.request.Request(f"{BASE}/repos/{REPO}/contents/{PATH}", + headers={"Authorization": f"token {TOKEN}"}) +info = json.loads(urllib.request.urlopen(req).read()) +sha = info["sha"] + +# 2. Read new content and encode +with open("/path/to/local/file") as f: + content = base64.b64encode(f.read().encode()).decode() + +# 3. PUT new content +data = json.dumps({"message": "update file", "content": content, "sha": sha}).encode() +req2 = urllib.request.Request(f"{BASE}/repos/{REPO}/contents/{PATH}", + data=data, method="PUT", + headers={"Authorization": f"token {TOKEN}", "Content-Type": "application/json"}) +urllib.request.urlopen(req2) +print("done") +PYEOF +``` + +Always generate base64 from the actual file — never copy b64 strings from session history (they can be silently corrupted by terminal line wrapping). + +For Node.js scripts passed via `bastille cmd node sh -c '...'`, use base64 to avoid quoting issues: + +```sh +# On local machine: encode the script +cat script.js | base64 | tr -d '\n' +# On server: decode and run +echo | b64decode -r > /tmp/script.js && node /tmp/script.js +```