add Gitea-style admonitions, code annotations, callouts

Make the things that matter stand out, matching the Gitea callout look:
- extra.css: brand-orange warning admonitions (#f57c00, the lambda orange),
  hotter danger (#e8590c), blue note/info and cyan tip/example, plus blue
  code-annotation markers.
- deploy.md: footgun -> danger callout, secrets -> orange warning, eligible
  -> tip; Dockerfile and compose gain numbered code annotations explaining
  each magic line; paths/mounts as a table; a restart snippet for rotation.
- workflow.md: warning on setting per-repo git identity before first commit;
  tip elevating verify-by-executing.

Verified in-browser: computed border colors match (warning #f57c00,
danger #e8590c), 4 annotation markers render in brand blue, admonition
icons + tinted headers match the Gitea style. mkdocs build --strict clean.

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
disqualifier 2026-06-29 20:16:02 -04:00
parent 142a0dbff6
commit 8fc81fc4f4
3 changed files with 131 additions and 41 deletions

View File

@ -4,76 +4,88 @@
> **rethink-net** — our fleet of Ubuntu 26.x servers, ready to host whatever > **rethink-net** — our fleet of Ubuntu 26.x servers, ready to host whatever
> you've built. > you've built.
**Eligible:** APIs, websites, applets, bots, monitors. !!! tip "Eligible"
APIs, websites, applets, bots, monitors. The whole network runs on a few
The whole network runs on a few simple, consistent rules. Get your container to simple, consistent rules — get your container to follow them and deploying is
follow them and deploying is mostly handing us a `compose.yaml`. mostly handing us a `compose.yaml`.
## Docker — the services account ## Docker — the services account
Every service runs containerized as the shared **`services`** account: Every service runs containerized as the shared **`services`** account:
**uid/gid 1337**, fixed fleet-wide. Build your image to be **uid-agnostic** so it **uid/gid 1337**, fixed fleet-wide. Build your image to be **uid-agnostic** so it
runs cleanly as that account: runs cleanly as that account.
- `user: "1337:1337"` in compose.
- `chmod -R a+rwX /app` in the Dockerfile (covers non-mounted dirs — see the
bind-mount note below).
- `HOME=/tmp`.
- **No** in-container `user`/`useradd` — don't bake a user into the image.
```dockerfile ```dockerfile
FROM python:3.12-slim FROM python:3.12-slim
ENV HOME=/tmp ENV HOME=/tmp # (1)!
WORKDIR /app WORKDIR /app
# git in the build if you pip-install from git RUN apt-get update \
RUN apt-get update && apt-get install -y --no-install-recommends git \ && apt-get install -y --no-install-recommends git \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/* # (2)!
COPY . . COPY . .
RUN pip install --no-cache-dir . \ RUN pip install --no-cache-dir . \
&& chmod -R a+rwX /app && chmod -R a+rwX /app # (3)!
CMD ["python", "-m", "yourapp"] CMD ["python", "-m", "yourapp"]
``` ```
1. `HOME=/tmp` — the `services` account has no home dir; anything writing to
`$HOME` (caches, configs) needs a writable target.
2. Install `git` **only if** you `pip install` from git, then clean the apt lists
to keep the image small.
3. `chmod -R a+rwX /app` makes the app tree writable by **any** uid — this is
what "uid-agnostic" means. Note it only covers **non-mounted** dirs (see the
[footgun](#permissions-the-bind-mount-footgun)).
```yaml ```yaml
services: services:
yourapp: yourapp:
build: . build: .
user: "1337:1337" user: "1337:1337" # (1)!
environment: environment:
HOME: /tmp HOME: /tmp
volumes: volumes:
- /srv/configs/<project>:/app/config:ro # host-managed, read-only - /srv/configs/<project>:/app/config:ro # (2)!
- /srv/<dev>/<project>:/app/logs # live + rolled logs - /srv/<dev>/<project>:/app/logs # (3)!
- yourapp-data:/app/data # named volume — the rest - yourapp-data:/app/data # (4)!
volumes: volumes:
yourapp-data: yourapp-data:
``` ```
1. Run as the shared account. **No** in-container `user`/`useradd` — don't bake a
user into the image; set it here.
2. Configs: host-managed bind mount, mounted **read-only**.
3. Logs: bind mount — live and rolled, scraped for monitoring.
4. Everything else: a **named volume**. Docker owns it, so there are no host
permissions to fiddle with.
## Paths and mounts ## Paths and mounts
- **Configs**`/srv/configs/<project>/` — bind mount, host-managed. | What | Where | How |
- **Logs**`/srv/<dev>/<project>/` — bind mount; live and rolled, scraped for | --- | --- | --- |
monitoring. | Configs | `/srv/configs/<project>/` | bind mount, host-managed |
- **Everything else** (caches, browser profiles, scratch) → **named volumes**. | Logs | `/srv/<dev>/<project>/` | bind mount; live + rolled, scraped |
Docker manages ownership, so there are no host permissions to fiddle with. | Caches, profiles, scratch | named volume | Docker manages ownership |
## Permissions — the bind-mount footgun ## Permissions — the bind-mount footgun
!!! danger "Bind-mount sources must exist and be 1337-owned *before* `up`"
`docker compose up` does **not** create bind-mount directories as you. If a `docker compose up` does **not** create bind-mount directories as you. If a
bind-mount source is missing, the Docker daemon (**root**) creates it **as bind-mount source is missing, the Docker daemon (**root**) creates it **as
root** — and your container (**1337**) then can't write it. Logs fall back to root** — and your container (**1337**) then can't write it. Logs silently fall
console-only, caches re-download every run. back to console-only; caches re-download every run.
So: So:
- **Bind-mount sources (configs, logs) must EXIST and be 1337-owned _before_ - **Bind-mount sources (configs, logs) must EXIST and be 1337-owned before `up`**
`up`.** This is handled at provisioning, not a per-deploy chown hook. handled at provisioning, not a per-deploy chown hook.
- **Named volumes avoid this entirely** — use them for anything that doesn't need - **Named volumes avoid this entirely** — use them for anything that doesn't need
host visibility. host visibility.
- The Dockerfile `chmod -R a+rwX /app` only covers **non-mounted** dirs. A bind
mount overrides the image directory with the host directory, so for mounted !!! note "Why `chmod a+rwX /app` doesn't save you here"
paths the **host-side ownership wins**. The Dockerfile `chmod` only covers **non-mounted** dirs. A bind mount
overrides the image directory with the host directory, so for mounted paths
the **host-side ownership wins** — the image's permissions are irrelevant.
## What your compose / Dockerfile needs ## What your compose / Dockerfile needs
@ -87,12 +99,15 @@ So:
## Secrets ## Secrets
We do **not** commit secrets (usually, lol). The rule: !!! warning "Secrets never go in the image"
We do **not** commit secrets (usually, lol). They stay **gitignored**, live on
- Secrets stay **gitignored**. the host at `/srv/configs/<project>/`, and are bind-mounted **read-only** at
- They're placed on the host at `/srv/configs/<project>/`. runtime. Add them to `.dockerignore` so a `COPY . .` can't sweep them into a
- They're bind-mounted **read-only** at runtime. layer.
- **Never** baked into the image — add them to `.dockerignore` so `COPY . .`
can't grab them.
Rotating a secret = edit the host file and restart. No rebuild. Rotating a secret = edit the host file and restart. No rebuild.
```bash
vim /srv/configs/<project>/secrets.env # edit on the host
docker compose restart yourapp # pick up the change — no rebuild
```

View File

@ -93,6 +93,70 @@
color: rgba(238, 241, 246, 0.6); color: rgba(238, 241, 246, 0.6);
} }
/* Admonitions Gitea-style: brand-orange warning, blue note, etc.
The lambda logo's orange (#ffae42 -> #f57c00) drives the warning/danger look. */
.md-typeset .admonition,
.md-typeset details {
border-width: 0 0 0 .2rem;
background-color: var(--rt-surface);
}
/* warning / caution: the orange Gitea callout */
.md-typeset .admonition.warning,
.md-typeset details.warning,
.md-typeset .admonition.caution,
.md-typeset details.caution {
border-color: #f57c00;
}
.md-typeset .warning > .admonition-title,
.md-typeset .warning > summary,
.md-typeset .caution > .admonition-title,
.md-typeset .caution > summary {
background-color: rgba(245, 124, 0, 0.12);
}
.md-typeset .warning > .admonition-title::before,
.md-typeset .caution > .admonition-title::before {
background-color: #f57c00;
}
/* danger: a hotter orange-red for true footguns */
.md-typeset .admonition.danger,
.md-typeset details.danger {
border-color: #e8590c;
}
.md-typeset .danger > .admonition-title,
.md-typeset .danger > summary {
background-color: rgba(232, 89, 12, 0.14);
}
/* note / info / tip: blue + cyan from the rethink palette */
.md-typeset .admonition.note,
.md-typeset details.note,
.md-typeset .admonition.info,
.md-typeset details.info {
border-color: var(--rt-primary);
}
.md-typeset .note > .admonition-title,
.md-typeset .info > .admonition-title {
background-color: rgba(86, 155, 204, 0.12);
}
.md-typeset .admonition.tip,
.md-typeset details.tip,
.md-typeset .admonition.example,
.md-typeset details.example {
border-color: var(--rt-accent);
}
.md-typeset .tip > .admonition-title,
.md-typeset .example > summary {
background-color: rgba(85, 187, 255, 0.1);
}
/* Code annotation markers (the (1) callouts) in brand blue. */
.md-typeset .md-annotation__index {
background-color: var(--rt-primary);
color: var(--rt-body);
}
/* Landing-page cards: surface tint + blue border like Gitea panels. */ /* Landing-page cards: surface tint + blue border like Gitea panels. */
.md-typeset .grid.cards > :is(ul, ol) > li, .md-typeset .grid.cards > :is(ul, ol) > li,
.md-typeset .grid > .card { .md-typeset .grid > .card {

View File

@ -68,6 +68,12 @@ Our conventions, in short:
- **Libraries install from git by tag** — pin a version in your deps, bump the - **Libraries install from git by tag** — pin a version in your deps, bump the
tag when the lib releases. tag when the lib releases.
!!! warning "Set your per-repo identity *before* the first commit"
One machine often carries more than one Gitea identity/key. If you forget to
run `gitsetup` in a fresh clone, your commits attribute to the wrong
user — or push with the wrong key and bounce. Run it right after cloning;
see [per-project git identity](#handy-shell-setup).
## Git basics (our-flavored) ## Git basics (our-flavored)
Not a git tutorial — just how the everyday loop looks against our Gitea. For the Not a git tutorial — just how the everyday loop looks against our Gitea. For the
@ -154,6 +160,11 @@ git diff | clip.exe # then paste the diff straight into the chat
Use AI for the plan and the spec, let the agent build, and always **prove it Use AI for the plan and the spec, let the agent build, and always **prove it
works by running it** — don't accept "this should work." works by running it** — don't accept "this should work."
!!! tip "Verify by executing, not asserting"
The single habit that separates good AI-assisted work from plausible-looking
nonsense: **run it**. A passing build, a real screenshot, actual output — that
is proof. "It should work" is not.
## Recommended setup ## Recommended setup
- **WSL2 (Ubuntu) + VS Code + Claude Code.** Develop in WSL, edit in VS Code over - **WSL2 (Ubuntu) + VS Code + Claude Code.** Develop in WSL, edit in VS Code over