initial commit

This commit is contained in:
Daniel Noulet 2026-10-07 15:03:50 +02:00
commit 23816d00f7
71 changed files with 14497 additions and 0 deletions

229
data/docs/go-doc.md Normal file
View file

@ -0,0 +1,229 @@
# go-doc
**go-doc** is a flat-file documentation and audience-feedback platform. It isolates guidance for
three audiences inside a single Markdown document, lets anyone annotate that document as a **User 🟢**,
**Operator 🔵** or **Developer 🔴**, and stores everything as plain files you can read, diff and commit.
```
data/docs/go-doc.md # raw Markdown, one file per document
data/comments/go-doc-comments.json # JSON array of audience feedback
```
One file carries all three viewpoints. Sections are delimited by level-2 headings and classified by the
audience they address, so the reader renders the tab that matters to you while the shared preamble
stays visible to everyone. Feedback badges are derived **server-side** from the stored role, so a
client cannot spoof another audience's colour by submitting a `badge` field.
---
## For Users
You use the product. You are not deploying it and you are not extending it — you want the shortest
path from "I have an account" to "the thing I came here to do actually worked".
### Getting oriented
Open the app and you land on the document index: every Markdown file in `data/docs/`, newest first.
Each entry shows its size, last modification date and how much audience feedback it has collected.
### Switching to your view
Every document has three tabs along the top:
| Tab | Badge | What it shows you |
| --- | --- | --- |
| User | 🟢 | Day-to-day workflows and everyday tasks |
| Operator | 🔵 | Deployment, health checks, rollback |
| Developer | 🔴 | API contracts, schemas, integration notes |
Switching tabs swaps the guidance without duplicating content across files. The preamble above —
this section — always stays visible.
### Leaving feedback
The right-hand panel is your feedback channel. Pick the badge that matches who you are, write your
note, and post it. It is stored as one entry in a JSON array next to the document, so your note is
never lost and never overwritten by someone else's.
Filter the list by any badge to focus on the feedback relevant to you. Comments carry the author,
the role, the badge and a timestamp, and you can delete your own.
### Editing a document
Press **Edit** to open the hybrid editor. It has two synchronised views:
- **Visual Edit (WYSIWYG)** — a rich surface where bold, italic, inline code, headings, lists,
quotes and links are applied directly. The Markdown underneath updates as you type.
- **Source Code (Markdown)** — the raw Markdown with a live preview beside it.
Both views write to the same string, so you can start in one and finish in the other. `Ctrl/Cmd+S`
saves.
---
## For Operators
You keep the thing running. Your concern is state convergence, blast radius, and how fast you can
get back to a known-good position when a release goes wrong.
### Storage layout
State is a directory, not a database:
```
data/docs/<slug>.md # one Markdown file per document
data/comments/<slug>-comments.json # one JSON array per document
```
There is no daemon to restart and no migration to run. Backups are file copies; the directory layout
*is* the schema.
### Write guarantees
Every mutation is a temp-file write, `fsync`, `rename`, then `fsync` of the parent directory. The
consequences matter operationally:
- A reader never observes a half-written document.
- A crash mid-write leaves either the previous file or the new one — never a truncated one.
- Feedback read-modify-write cycles are serialised per slug, so a burst of comments cannot interleave
and lose entries.
Temporary files are named `.godoc-tmp-*` and are excluded from the document index. If you ever see
one survive a crash, that directory is safe to inspect and safe to delete.
### Health checks
```bash
curl -fsS http://localhost:8080/api/health
```
Returns `{"status":"ok","data_root":"...","time":"..."}`. If the process answers at all, the data
directory was opened successfully at boot.
### Store-wide census
`GET /api/stats` reports total documents, total comments and a per-role breakdown — the fastest way
to confirm a restore or a sync actually landed:
```json
{ "docs": 120, "comments": 3000, "by_role": { "user": 1000, "operator": 1000, "developer": 1000 } }
```
### Rollback
There is nothing to roll back except the files. Restore the previous `data/` snapshot, restart the
process, and confirm with the index endpoint. Deletes are permanent and are not journalled, so keep
your backups.
---
## For Developers
You integrate with it or you change it. You care about contracts, determinism, and failure modes.
### REST API
| Method | Path | Purpose |
| --- | --- | --- |
| `GET` | `/api/health` | Liveness and the resolved data root |
| `GET` | `/api/roles` | Role → badge → colour catalogue for the UI |
| `GET` | `/api/docs` | Index; `?q=` searches, `?counts=1` adds comment counts |
| `GET` | `/api/docs/:slug` | Raw Markdown (`text/markdown`), or `?format=json` |
| `POST` | `/api/docs/:slug` | Create or fully replace; `text/markdown` or JSON |
| `PUT` | `/api/docs/:slug` | Alias of `POST` |
| `DELETE` | `/api/docs/:slug` | Permanent delete; cascades feedback |
| `GET` | `/api/docs/:slug/comments` | JSON list of comments |
| `POST` | `/api/docs/:slug/comments` | Append `{author, role, content}` |
| `DELETE` | `/api/docs/:slug/comments/:id` | Remove one comment |
| `GET` | `/api/docs/:slug/stats` | Totals, per-role tally, time bounds |
| `GET` | `/api/stats` | Store-wide totals |
### Slug contract
Titles are slugified into lower-case, hyphen-separated identifiers:
```
"Core Service API" -> core-service-api
"Überwachen der Dienste" -> uberwachen-der-dienste
"API_v2 (Internal)" -> api-v2-internal
"!!!": -> rejected
```
Diacritics fold to ASCII, separator runs collapse to a single hyphen, the result is trimmed and
capped at 120 characters. Slugs containing `/`, `\`, `..`, control characters or NUL bytes are
rejected with `400 invalid_slug` before any filesystem call, so a URL can never escape `data/docs`.
Because normalisation is idempotent, `/api/docs/Core Service API` and `/api/docs/core-service-api`
address the same document. The `POST` response echoes the canonical slug — always use that value.
### Comment contract
`POST /api/docs/:slug/comments` accepts:
```json
{ "author": "ana", "role": "Developer", "content": "The retry budget is undocumented." }
```
The stored record is authoritative — the badge is derived from the role, and an incoming `badge`
field is ignored:
```json
{
"id": "c1a105f4684c_9b1c081def3b00a4",
"author": "ana",
"role": "developer",
"badge": "🔴",
"content": "The retry budget is undocumented.",
"timestamp": "2026-10-04T08:07:45.996Z"
}
```
Roles are canonicalised case-insensitively (`Developer`, `DEV`, `developer` are the same role) and
anything else returns `400 invalid_role`. Empty content returns `400 invalid_comment`. An unset
author defaults to the role label. IDs are a millisecond prefix plus 8 random bytes, so they sort
roughly chronologically and do not collide under load.
### Error shapes
Every failure is JSON, so clients never have to parse an HTML error page:
```json
{
"error": { "code": "invalid_role", "message": "storage: invalid role: \"root\" ...", "request_id": "" },
"status": 400
}
```
Codes: `not_found`, `invalid_slug`, `invalid_role`, `invalid_comment`, `invalid_body`,
`body_too_large`, `internal_error`.
### Limits
| Limit | Value |
| --- | --- |
| Document body | 5 MiB |
| Comment body | 256 KiB |
| Slug length | 120 characters |
| Author length | 120 characters |
Exceeding the document or comment ceiling returns `400 body_too_large` and writes nothing.
### Embedding the SPA
`make build` copies `frontend/dist` into `backend/webui/dist`, where `//go:embed all:dist` picks it
up. The compiled binary then serves the SPA on `/` and the API on `/api/*` from one process with no
runtime dependencies. Client-side routes such as `/docs/core-service-api` fall back to `index.html`;
`/api/*` never does.
---
## Related Documentation
Other go-* projects documented in this workspace:
- [go-bananas](go-bananas.md) — Runtime design-token service
- [go-guess](go-guess.md) — Infrastructure guessing game
- [go-loose](go-loose.md) — Lightweight auth middleware
- [go-mad](go-mad.md) — Markdown-driven development
- [go-places](go-places.md) — Location-aware services
- [go-tell](go-tell.md) — Content CMS