Files
bincio-wiki/CLAUDE.md
T
brutsalvadi a85a2eeb6d Fix sidecar: advance refs/heads/main after each commit
post-receive uses `git checkout -f <SHA>` which detaches HEAD in the bare
repo. Without this fix, sidecar commits advance the detached HEAD but not
refs/heads/main and are orphaned on the next push.

Also commits CLAUDE.md docs update and the retry-loop sync-vps.sh.
2026-05-08 08:57:55 +02:00

6.5 KiB

bincio_wiki — development notes

Project overview

A private wiki for the bincio group of friends. Built on Astro 6 using the brutsalvadi/astro-bloomz fork as a git submodule at site/. Content lives in pages/ and blog/ at the repo root; Astro loads them via glob loaders pointing to ../pages and ../blog (outside the submodule). No symlinks.

Directory layout

bincio_wiki/
  pages/          wiki content (*.md files the community edits)
    _docs/        documentation shown as a separate section
  blog/           blog/stories content (*.md files)
  config/         bincio-specific configuration (outside site/ submodule)
    sections.json wiki sections and subsections (imported by Astro pages + editor)
    i.bonsai.md   wikibonsai semantic tree (loaded by the index collection)
  assets/         user-uploaded images (gitignored; rsync'd to VPS separately)
  site/           Astro 6 app (git submodule → brutsalvadi/astro-bloomz)
  edit/           FastAPI edit server (Python, port 8001)
  pyproject.toml  Python dependencies (managed by uv)
  uv.lock         uv lockfile (committed)
  scripts/        dev.sh — starts both Astro and FastAPI together
  docs/           repo-level documentation (not served as wiki pages)

Running locally

bash scripts/dev.sh          # Astro only (port 4321)
bash scripts/dev.sh --edit   # Astro + FastAPI edit sidecar (port 8001)

Python dependencies are managed by uv. uv sync is called automatically by dev.sh --edit — no manual setup needed. To add a dependency: uv add <package> (updates pyproject.toml and uv.lock).

Content architecture

  • Wiki pages: pages/*.md (IDs in Astro content store are the bare filename without extension, e.g. digital-garden)
  • Docs section: pages/_docs/*.md (IDs start with _docs/)
  • Blog/stories: blog/*.md
  • Assets: assets/ — gitignored, not part of the Astro build. FastAPI serves them at /assets/{filename} in dev (Vite proxies /assets → port 8001). On the VPS, nginx serves /assets/ directly from the filesystem (rsync'd separately). Upload via POST /api/assets (multipart); returns {"url": "/assets/filename"}. The editor inserts ![name](/assets/filename) into the markdown on upload.
  • Bonsai index: stored in site/src/content/index/ — the i.bonsai.md file defines the semantic tree structure
  • The homepage (site/src/pages/index.astro) splits entries by _docs/ prefix into "Pagine recenti" and "Documentazione" sections

Astro 6 content layer notes

  • Collections use glob loaders in site/src/content.config.ts
  • entries collection base is ../pages (relative to site/), resolving to bincio_wiki/pages/. Vite is configured with server.fs.allow: ['..'] to permit serving files from outside the project root.
  • blog collection base is ../blog (same pattern)
  • index collection base is ./src/content/index (stays inside site/)
  • Custom generateId on entries and index — see content.config.ts for details. index preserves dots so i.bonsai is not mangled by githubSlug.
  • Use entry.id not entry.slug
  • Use import { render } from 'astro:content'; render(entry) not entry.render()

Language / i18n

Italian is the default language. All documentation in pages/_docs/ is in Italian. Technical terms (WikiRefs, Astro, plugin names, wikilink syntax, code) stay in English. See docs/lingua.md for the full rationale.

Authentication

Current approach (same as bincio_activity)

Not yet implemented in bincio_wiki — pending.

Planned pattern:

  • nginx serves the static Astro build publicly (no nginx-level auth)
  • Every Astro page layout calls GET /api/me on load
  • If the API returns 401/error, the page JS redirects to /login/
  • FastAPI (edit/server.py) handles sessions: SQLite users table, bcrypt passwords, HTTP-only session cookie
  • Login page at /login/ — a static Astro page that POSTs credentials to /api/auth/login

Security assessment

This is client-side enforcement only. The HTML is technically accessible to anyone who:

  • Uses curl or wget directly
  • Disables JavaScript in the browser
  • Views page source

For bincio_wiki this is an acceptable tradeoff. The content is community memories and shared activities (not financial/medical data), members are not adversarial, and the goal is keeping casual visitors and search engine crawlers out — not resisting determined attackers.

Future: making specific pages public

With this architecture, making a page public later is trivial: just use a layout variant that skips the /api/me check. No nginx changes needed since nginx already serves everything publicly at the static file level.

For stricter enforcement on genuinely sensitive pages, the alternative is Astro SSR with a server middleware that checks the session cookie before rendering any HTML. This would require migrating from static output to SSR mode — a bigger change, not warranted for now.

VPS deployment target

Debian 12 VPS. nginx serves the built site from /var/www/bincio/wiki/. FastAPI proxied from localhost:8001 via nginx location /api/ { proxy_pass }. See docs/vps.md for full nginx config and deployment steps.

Edit concurrency — future work

Current state: last-write-wins. No conflict detection.

Planned approach (not yet implemented):

  1. Git commits with attribution — on every save, git add <file> + git commit -m "handle: edited page-name" --author="handle <handle@bincio.wiki>". Requires an asyncio.Lock in server.py to serialize git operations.

  2. Optimistic locking via base-hash check — when the client loads a page for editing, the server returns the current git commit hash alongside the content. On save, the client sends that hash back. If HEAD has moved for that file since then, the save is rejected with a 409 and a "pagina modificata, ricarica prima di salvare" message. No lock to release, no keepalive needed — the user can abandon the editor freely.

  3. 3-way merge (ideal) — instead of rejecting on conflict, use git merge-file with base = content at baseHash, ours = current HEAD, theirs = user's new content. Different lines → automatic merge, one commit. Same lines → 409 conflict error. Builds on step 2; adds it when needed.

No pessimistic locking needed. Optimistic locking with 3-way merge handles concurrent edits gracefully without timers or "are you still there?" prompts.

Git conventions

  • No Co-Authored-By: Claude trailers in commits
  • Commit messages in English
  • Do not push without explicit user instruction