Demo content from demo/admin-guide (0c17989)

This commit is contained in:
f451-admin 2026-09-29 18:52:56 +02:00
parent 8385e2024a
commit 3c566c0cc9
11 changed files with 645 additions and 2 deletions

8
.order Normal file
View file

@ -0,0 +1,8 @@
sign-in-options
entra-id
forgejo-identity-provider
github-sign-in
spaces-and-git-providers
configuration-reference
backup-and-restore
operations

View file

@ -1,2 +0,0 @@
# admin-guide

View file

@ -0,0 +1,69 @@
---
id: backup-and-restore
title: Backup and restore
description: What actually needs a backup — Git holds the content, the database only holds a derived index.
tags: [admin, backup, restore]
lang: en
---
# Backup and restore
f451's split between "Git is the source of truth" and "Postgres is only an
index" (see *Principles* in the Developer Guide) turns backup planning
into a short list, not a long one.
## The one thing that matters
> [!IMPORTANT]
> **The Forgejo data volume is the only backup-critical piece of a whole
> f451 deployment.** It holds every space's repository content plus
> Forgejo's own configuration and account database. Losing it without a
> backup means the wiki content is gone — there is nothing else it can be
> rebuilt from.
If a space lives on GitHub instead of the bundled Forgejo, that content is
already backed up by GitHub itself; only Forgejo-hosted spaces are this
deployment's own responsibility.
## The database is a rebuildable cache
The Postgres volume holds the search index, rendered page content, the
knowledge-graph edges, sessions, and encrypted provider tokens. All of the
content-derived pieces are rebuilt automatically:
- `POST /admin/reindex` (bearer-token-protected, see
[[operations]]) rebuilds the full index for one or all spaces from Git,
on demand.
- The drift job (see [[spaces-and-git-providers]]) rebuilds it
automatically within about five minutes of a missed webhook, without any
manual step.
Losing the database without a backup costs two things, neither of them
content:
- **Sessions** — everyone has to sign in again.
- **Encrypted provider tokens** — everyone has to reconnect their Forgejo
or GitHub account once (see [[sign-in-options]]).
A database backup is still worth having in practice, purely to skip the
reindex time and avoid the mass reconnect — but it is never the *only*
copy of anything.
## Restore, in outline
1. Stop both stacks (the Git stack and the application stack).
2. Restore the Forgejo data volume from backup.
3. Start the Git stack and wait for it to report healthy.
4. Start the application stack. The database starts empty — apply pending
migrations before traffic reaches the new containers (the production
image ships a compiled migration runner for exactly this).
5. Trigger a full reindex of all spaces (`POST /admin/reindex`) rather than
waiting for the drift job — after a restore there is no reason to wait
up to five minutes for content to reappear.
6. Verify: the space list responds, a known page is readable, and
`GET /admin/status` reports no indexing errors for the reindex that just
ran.
Sessions and account connections are deliberately not part of this
procedure — people sign in again and, if needed, reconnect their Git
account once, exactly as described above.

View file

@ -0,0 +1,123 @@
---
id: configuration-reference
title: Configuration reference
description: Every environment variable the api service reads, grouped by concern.
tags: [admin, reference, configuration]
lang: en
---
# Configuration reference
All variables below are read by the `api` service. None are required to
start f451 at all — an instance with nothing set runs in a minimal,
read-only, unauthenticated mode (`/healthz`/`/readyz` only). Each group
becomes meaningful once you set its first variable.
## Spaces and providers
See [[spaces-and-git-providers]] for the full explanation.
| Variable | Required when | Default |
|---|---|---|
| `F451_SPACES` | Any space should be visible | — (minimal mode) |
| `F451_FORGEJO_URL` | A space uses `provider: "forgejo"`, or Forgejo account linking is enabled | — |
| `F451_FORGEJO_TOKEN` | A space uses `provider: "forgejo"` | — |
| `F451_GITHUB_TOKEN` | A space uses `provider: "github"` | — |
| `F451_WEBHOOK_SECRET_FORGEJO` | Forgejo webhooks should be accepted | — |
| `F451_WEBHOOK_SECRET_GITHUB` | GitHub webhooks should be accepted | — |
| `F451_GLOBAL_TEMPLATES` | A provider-wide template repository is shared across spaces | — |
## Sign-in (OIDC)
See [[sign-in-options]], [[entra-id]], [[forgejo-identity-provider]].
| Variable | Required when | Default |
|---|---|---|
| `F451_OIDC_ISSUER` | Auth should be enabled at all — unset means the entire API is unauthenticated | — (auth off) |
| `F451_OIDC_CLIENT_ID` | `F451_OIDC_ISSUER` is set | — |
| `F451_OIDC_CLIENT_SECRET` | `F451_OIDC_ISSUER` is set | — |
| `F451_OIDC_REDIRECT_URL` | `F451_OIDC_ISSUER` is set — must equal `https://<host>/auth/callback` | — |
| `F451_OIDC_PROVIDER_NAME` | Never required | unset → plain "Sign in" button |
| `F451_TOKEN_KEY` | `F451_OIDC_ISSUER` is set — otherwise the api fails to start | — |
## Sign-in (GitHub)
See [[github-sign-in]].
| Variable | Required when | Default |
|---|---|---|
| `F451_GITHUB_LOGIN` | GitHub should appear as a sign-in method | `0` (off) |
| `F451_GITHUB_OAUTH_CLIENT_ID` | `F451_GITHUB_LOGIN=1` | — |
| `F451_GITHUB_OAUTH_CLIENT_SECRET` | `F451_GITHUB_LOGIN=1` | — |
## Account linking (reading and writing)
Independent of which method someone signed in with — see
[[sign-in-options]].
| Variable | Required when | Default |
|---|---|---|
| `F451_FORGEJO_OAUTH_CLIENT_ID` / `_SECRET` | "Connect Forgejo" should be offered (needs `F451_FORGEJO_URL` too) | — (linking off) |
| `F451_GITHUB_OAUTH_CLIENT_ID` / `_SECRET` | "Connect GitHub" should be offered | — (linking off) |
## Cookies and running more than one instance on a host
| Variable | Required when | Default |
|---|---|---|
| `F451_COOKIE_PREFIX` | A second instance shares a host with this one — browsers do not separate cookies by port | `f451` |
| `F451_WEB_PORT` | A second instance needs a different host port for the web UI | `8080` |
| `F451_DRAWIO_PORT` | A second instance needs a different host port for the diagram editor | `8081` |
| `F451_FORGEJO_HTTP_PORT` / `F451_FORGEJO_SSH_PORT` | A second Forgejo instance shares a host | Forgejo defaults |
| `F451_FORGEJO_ROOT_URL` | A second Forgejo instance shares a host | Forgejo default |
## Local development only
> [!WARNING]
> Never set either of these in production.
| Variable | Effect |
|---|---|
| `F451_INSECURE_COOKIES=1` | Drops `secure` on session cookies (plain HTTP). |
| `F451_OIDC_ALLOW_INSECURE=1` | Allows an `http://` OIDC issuer. |
## Admin and operations
See [[operations]] and [[backup-and-restore]].
| Variable | Required when | Default |
|---|---|---|
| `F451_ADMIN_TOKEN` | `POST /admin/reindex` and `GET /admin/status` should work at all — unset means both are fail-closed | — (disabled) |
| `F451_MAX_UPLOAD_MB` | A non-default media upload limit is needed | `10` |
## Rate limits and reverse proxy
| Variable | Required when | Default |
|---|---|---|
| `F451_RATE_LIMIT_AUTH_MAX` | A non-default auth rate limit is needed (per route, per client IP) | `10`/min |
| `F451_RATE_LIMIT_SEARCH_MAX` | A non-default search rate limit is needed | `60`/min |
| `F451_RATE_LIMIT_API_TOKEN_MAX` | A non-default limit for API-token traffic (MCP agents) is needed, per user across their tokens | `300`/min |
| `F451_TRUST_PROXY` | This deployment always runs behind a reverse proxy — without it, every user shares one rate-limit bucket | — (no proxy trusted) |
> [!IMPORTANT]
> `F451_TRUST_PROXY` needs the number of trusted hops between the reverse
> proxy and the `api` service, not just "on" or "off". Setting it to `true`
> trusts the left-most `X-Forwarded-For` entry, which a client can set
> itself — that makes the rate limits trivially bypassable. Verify the
> correct hop count against your actual proxy chain before relying on the
> limits in production; see [[operations]].
## Public URL
| Variable | Required when | Default |
|---|---|---|
| `F451_PUBLIC_BASE_URL` | Production, effectively always | — (falls back to the request's `Host` header) |
`F451_PUBLIC_BASE_URL` is the exact scheme and host (no path) that users
type into their browser. It is the basis for the OAuth `redirect_uri` used
when connecting a Forgejo or GitHub account, and it is the "our own
origin" side of the CSRF origin check on every write request.
> [!WARNING]
> A wrong value here does not fail loudly at startup — it fails at the
> first save, with every mutating browser request rejected with `403`. See
> [[operations]] for this symptom in the troubleshooting table.

77
entra-id/index.md Normal file
View file

@ -0,0 +1,77 @@
---
id: entra-id
title: Microsoft Entra ID
description: Registering an app in Entra and pointing f451's OIDC configuration at it, step by step.
tags: [admin, auth, entra]
lang: en
---
# Microsoft Entra ID
Entra ID is a standard OpenID Connect provider from f451's point of view —
nothing here is Entra-specific in the code, only in the app registration
steps below.
## 1. Register an application
In the Entra admin center, create a new **app registration**:
- **Single tenant** (unless you specifically need multi-tenant sign-in).
- **Redirect URI**, platform *Web*: `https://<host>/auth/callback` — this
must match `F451_OIDC_REDIRECT_URL` exactly, including scheme and any
trailing slash.
- Under **Certificates & secrets**, create a **client secret**.
> [!WARNING]
> Client secrets expire. Note the expiry date somewhere your team actually
> looks (a calendar reminder, not just the Entra portal) — an expired
> secret takes sign-in down for everyone until it is rotated. Rotating it
> means generating a new secret in Entra and updating
> `F451_OIDC_CLIENT_SECRET`, then restarting the `api` service.
## 2. Configure the ID token claims
Recommended: add the optional claim **`email`** to the ID token (**Token
configuration** → **Add optional claim** → ID). f451 reads the user's
email from the `email` claim, falling back to `preferred_username` or
`upn` if either of those looks like an email address (contains `@`).
Without a usable email claim, sign-in still works, but the identity f451
records for that person is less predictable.
## 3. Restrict who may sign in
By default, anyone in the tenant with access to the app can sign in. To
restrict that, open the **enterprise application** for this app
registration and turn on **Assignment required** under **Properties**,
then assign the users or groups who should have access under **Users and
groups**. This is the only access control at the sign-in layer — it does
not grant access to any space; that still comes from the person's Forgejo
or GitHub permissions (see [[sign-in-options]]).
## 4. Configure f451
Set on the `api` service:
| Variable | Value |
|---|---|
| `F451_OIDC_ISSUER` | `https://login.microsoftonline.com/<tenant-id>/v2.0` |
| `F451_OIDC_CLIENT_ID` | the app registration's Application (client) ID |
| `F451_OIDC_CLIENT_SECRET` | the client secret from step 1 |
| `F451_OIDC_REDIRECT_URL` | `https://<host>/auth/callback` |
| `F451_TOKEN_KEY` | 32 random bytes, base64-encoded (`openssl rand -base64 32`) — encrypts stored provider tokens, independent of Entra |
| `F451_OIDC_PROVIDER_NAME` | optional, e.g. `Microsoft Entra` — labels the sign-in button "Sign in with Microsoft Entra"; unset shows a plain "Sign in" |
f451 requests the scopes `openid email profile` — no Entra-side API
permissions need to be granted beyond what a standard app registration
gets by default.
## Forgejo can use Entra too
If your Forgejo instance should show the same people as f451 without a
separate identity system, configure Entra as an **authentication source**
in Forgejo itself (Forgejo admin panel → Authentication Sources → add an
OAuth2/OIDC source pointed at the same Entra app, or a dedicated one).
Users then still take the one extra step of linking their Forgejo account
under **Settings → Connections** in f451 — Entra authenticating both
systems does not by itself connect them. See
[[forgejo-identity-provider]] for the setup that removes even that step.

View file

@ -0,0 +1,67 @@
---
id: forgejo-identity-provider
title: Forgejo as the identity provider
description: Using the bundled Forgejo's built-in OIDC provider for sign-in, and linking accounts in one step.
tags: [admin, auth, forgejo]
lang: en
---
# Forgejo as the identity provider
Forgejo ships a built-in OpenID Connect provider
(`/.well-known/openid-configuration`). f451 can point its one configured
OIDC issuer at it instead of an external identity provider — useful for
local development, and viable for production if Forgejo is already your
single source of identity (see [[entra-id]] for federating Forgejo's own
accounts to Entra, so user management still lives in one place).
## Create the OAuth2 application in Forgejo
In Forgejo, as an administrator (or as the account that should own the
app): **Settings → Applications → OAuth2 Applications**, create a new
application with redirect URI `https://<host>/auth/callback`. Forgejo
gives you a **Client ID** and **Client Secret** — set these as
`F451_OIDC_CLIENT_ID` / `F451_OIDC_CLIENT_SECRET`, and set
`F451_OIDC_ISSUER` to the Forgejo instance's base URL.
## The one-app trick: sign-in and account linking together
Normally, signing in and connecting a Forgejo account (for reading and
writing spaces, see [[sign-in-options]]) are two separate steps: sign in
via whatever OIDC provider is configured, then separately connect Forgejo
under **Settings → Connections**.
> [!TIP]
> If f451's OIDC client (`F451_OIDC_CLIENT_ID`/`_SECRET`) and the Forgejo
> account-linking connection (`F451_FORGEJO_OAUTH_CLIENT_ID`/`_SECRET`)
> point at the **same** Forgejo OAuth2 application, the first sign-in
> already links the Forgejo account — there is no second step. This is
> exactly the setup `scripts/dev-local-setup.sh` creates for local
> development.
Using two different Forgejo OAuth2 applications for the two purposes also
works — it just means a person signs in, then still has to connect Forgejo
separately before they see any space.
## User management lives in Forgejo
Once Forgejo is the identity source, account lifecycle — creating
accounts, password resets, two-factor authentication, or federating a
further upstream source like LDAP — happens entirely in Forgejo's own
admin panel. f451 does not duplicate any of it; it only consumes the
resulting OIDC identity.
## Local-only settings
Running Forgejo as the identity provider over plain HTTP (typical for
local development) needs two additional variables on the `api` service:
| Variable | Effect |
|---|---|
| `F451_INSECURE_COOKIES=1` | Drops the `secure` flag on session cookies so they survive over HTTP. |
| `F451_OIDC_ALLOW_INSECURE=1` | Allows an `http://` issuer during OIDC discovery. |
> [!WARNING]
> Both are for local HTTP development only. Never set either in
> production — a production Entra issuer only ever speaks HTTPS anyway, so
> there is nothing to gain and a cookie without `secure` to lose.

48
github-sign-in/index.md Normal file
View file

@ -0,0 +1,48 @@
---
id: github-sign-in
title: GitHub sign-in
description: Enabling GitHub as a sign-in method, independent of or alongside OIDC.
tags: [admin, auth, github]
lang: en
---
# GitHub sign-in
GitHub can be enabled as its own sign-in method, separate from the single
configured OIDC provider (see [[sign-in-options]]). It works whether or
not an OIDC provider is configured at all.
## Enable it
| Variable | Effect |
|---|---|
| `F451_GITHUB_LOGIN` | Set to `1` to turn on "Sign in with GitHub". |
| `F451_GITHUB_OAUTH_CLIENT_ID` | Client ID of a GitHub OAuth app. |
| `F451_GITHUB_OAUTH_CLIENT_SECRET` | Client secret of the same app. |
## Register the OAuth app on GitHub
Under `https://github.com/settings/developers`, create an **OAuth App**
with **Authorization callback URL** set to `https://<host>/auth/`. Use
exactly that path, not a more specific one — it must cover both
`/auth/github/callback` (sign-in) and `/auth/connect/github/callback`
(account linking, used when GitHub is only being connected rather than
signed in with).
## Sign-in also links the account
Signing in with GitHub links the GitHub account in the same step — there
is no separate "connect GitHub" action needed afterwards, unlike signing
in through OIDC and connecting a Git account separately (see
[[sign-in-options]]).
## One person, two users
> [!WARNING]
> f451 has no concept of "the same person across sign-in methods." Someone
> who signs in once via your OIDC provider and once via GitHub becomes
> **two** separate f451 users — with separate sessions and separate sets of
> connected accounts. This is rarely a problem in practice (most people use
> one method consistently), but worth knowing before you enable a second
> method for a team that already uses the first: tell people to pick one
> method and stick with it, rather than discovering the split themselves.

46
index.md Normal file
View file

@ -0,0 +1,46 @@
---
id: admin-home
title: Admin Guide
description: Deploying and running f451 — sign-in setup, spaces, configuration, backup, and day-to-day operations.
tags: [admin, guide]
lang: en
---
# Admin Guide
f451 is git-native: every space is a repository on Forgejo or GitHub, every
page a Markdown file in it. Postgres holds only a derived index (rendered
content, search vectors, graph edges) plus sessions and encrypted provider
tokens — nothing that lives exclusively in the database is irreplaceable.
That single fact shapes most of the operational decisions in this guide,
especially backup and restore.
This guide is for the people who deploy and run an instance, not for people
writing pages in it — for that, see the User Guide space.
## How this guide is organised
- [[sign-in-options]] — the identity providers f451 supports, and why there
are no local accounts
- [[entra-id]] — step-by-step setup with Microsoft Entra ID
- [[forgejo-identity-provider]] — using the bundled Forgejo as the identity
source, and linking accounts in one step
- [[github-sign-in]] — enabling GitHub as a sign-in method
- [[spaces-and-git-providers]] — configuring spaces, service tokens,
webhooks, and the drift job
- [[configuration-reference]] — every environment variable, grouped by
concern
- [[backup-and-restore]] — what actually needs a backup, and what a reindex
rebuilds for you
- [[operations]] — health checks, troubleshooting, and running two
instances on one host
> [!NOTE]
> **f451 has no role system of its own.** Who may sign in is decided by
> your identity provider; what a signed-in person can see and write is
> decided entirely by their linked Git account's permissions on the
> repository behind each space. See [[sign-in-options]].
If you are setting up sign-in for the first time, start with
[[sign-in-options]] to decide which provider fits your setup, then follow
the matching page.

72
operations/index.md Normal file
View file

@ -0,0 +1,72 @@
---
id: operations
title: Operations
description: Health checks, the admin reindex endpoint, a troubleshooting playbook, and running two instances on one host.
tags: [admin, operations, monitoring]
lang: en
---
# Operations
## Health and readiness
| Endpoint | Meaning |
|---|---|
| `GET /healthz` | Always `200` if the process is running — no database access. |
| `GET /readyz` | `200` if a query against Postgres succeeds, `503` otherwise. Use this for orchestration readiness probes. |
| `GET /admin/status` | Bearer-token-protected (`F451_ADMIN_TOKEN`, see [[configuration-reference]]). Returns in-memory counters (`webhookErrors`, `indexerErrors`, `driftErrors`) and a `since` timestamp. The counters reset on every process restart — treat this as "is something wrong right now", not as a historical audit log. |
## Triggering a reindex
```bash
curl -X POST https://<host>/admin/reindex \
-H "Authorization: Bearer $F451_ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{}'
```
An empty body reindexes every configured space; `{"space":"<space-id>"}`
reindexes just one. The response reports success or failure per space —
`200` if at least one space succeeded, `502` only if all of them failed
(check `GET /admin/status` and the `api` logs next).
> [!NOTE]
> If OIDC sign-in is active, `/admin/*` routes need a valid session **in
> addition to** the bearer token — a plain `curl` call without a browser
> session behind it gets a reproducible `401`. Run admin calls from a
> browser context where you are already signed in (developer tools'
> network/fetch panel with the session cookie present), not from a bare
> terminal.
## Troubleshooting playbook
| Symptom | Likely cause |
|---|---|
| Every API route is reachable without signing in | `F451_OIDC_ISSUER` is unset — auth is intentionally off in that state. |
| Sign-in redirects to an error page after the identity provider | `F451_OIDC_REDIRECT_URL` (or the app registration's redirect URI) doesn't match the actual callback URL exactly. |
| Every save/publish/release fails with `403` | `F451_PUBLIC_BASE_URL` doesn't match the URL users actually browse to — the CSRF origin check rejects the mismatch. See [[configuration-reference]]. |
| A webhook call fails with `401` | `F451_WEBHOOK_SECRET_FORGEJO`/`_GITHUB` doesn't match the secret configured on the repository's webhook. |
| A page edited directly in Git takes a few minutes to show up | Expected — the drift job catches up within about 5 minutes if the webhook was missed. See [[spaces-and-git-providers]]. |
| `POST /admin/reindex` / `GET /admin/status` returns `503` | `F451_ADMIN_TOKEN` is unset — both endpoints are fail-closed without it. |
| `POST /admin/reindex` returns `401` from a plain terminal call | Missing session — see the note above. |
| The rate limits seem to apply to "everyone at once" instead of per person | `F451_TRUST_PROXY` is unset or wrong for the real proxy chain — see [[configuration-reference]]. |
| The same person appears as two different users | They signed in through two different methods (e.g. OIDC once, GitHub once) — see [[github-sign-in]]. Not a bug, a consequence of having no shared identity across methods. |
| A user reports "I don't see any spaces" | Their account is signed in but has no linked Forgejo/GitHub account yet, or that account lacks repository access — see [[sign-in-options]]. |
## Running a second instance on the same host
Both the Forgejo stack and the application stack expose their ports and
cookie naming as variables, so a second instance (for example a demo or a
staging copy) can run alongside the first without conflicting:
| Variable | What it separates |
|---|---|
| `F451_COOKIE_PREFIX` | Session cookie names — without a distinct value, two instances sharing a host would overwrite each other's cookies in the browser. |
| `F451_WEB_PORT` | Host port for the web UI. |
| `F451_DRAWIO_PORT` | Host port for the diagram editor. |
| `F451_FORGEJO_HTTP_PORT` / `F451_FORGEJO_SSH_PORT` | Host ports for the second Forgejo instance. |
| `F451_FORGEJO_ROOT_URL` | The second Forgejo instance's own public URL. |
Give the second instance's Git stack its own Compose project name
(`docker compose -p <name> ...`) so its containers and volumes don't
collide with the first instance's.

60
sign-in-options/index.md Normal file
View file

@ -0,0 +1,60 @@
---
id: sign-in-options
title: Sign-in options
description: The identity providers f451 supports, how they compare, and why there are no local accounts.
tags: [admin, auth]
lang: en
---
# Sign-in options
f451 never stores a password. Every sign-in goes through an external
identity provider, and every permission check after that goes through the
Git provider behind the space a person is looking at — see *Principles* in the Developer Guide
in the developer guide for why that split exists. This page is about the
first half: who is allowed to sign in at all.
## Why no local accounts
A local account system would need its own password reset, its own
lockout policy, its own audit trail — none of which f451 could do better
than the identity provider your organisation already runs and already
trusts for offboarding. Instead, f451 delegates entirely: one configured
OIDC issuer, optionally GitHub as a second method, and permissions that
come from the linked Forgejo or GitHub account rather than from anything
f451 tracks itself.
## Comparison
| Provider | Use case | What f451 needs | Notes |
|---|---|---|---|
| **Microsoft Entra ID** | Production, organisation-managed identities | `F451_OIDC_ISSUER`/`_CLIENT_ID`/`_CLIENT_SECRET`/`_REDIRECT_URL`, `F451_TOKEN_KEY` | See [[entra-id]]. Sign-in only — reading/writing still needs a separately connected Forgejo or GitHub account, unless Forgejo itself also authenticates against Entra (see next row). |
| **Forgejo (bundled)** | Local development, or production where Forgejo is the single identity source | Same OIDC variables, pointed at Forgejo's built-in OIDC provider | See [[forgejo-identity-provider]]. Can also make the *first* sign-in link the Forgejo account automatically. |
| **Other OIDC provider** (Keycloak, Authentik, Zitadel, Okta, Google, …) | Any organisation already standardised on a different IdP | Same OIDC variables, pointed at that provider's issuer | Configuration only — f451 speaks standard OpenID Connect, nothing provider-specific. |
| **GitHub** | Teams whose spaces already live on GitHub | `F451_GITHUB_LOGIN=1`, `F451_GITHUB_OAUTH_CLIENT_ID`/`_SECRET` | See [[github-sign-in]]. Works with or without an OIDC provider configured; signing in with GitHub also links the GitHub account in the same step. |
Only one OIDC issuer can be configured at a time — f451 does not offer a
picker between several OIDC providers. GitHub sign-in is independent of
that and can be enabled alongside it, or on its own.
## What sign-in does and does not unlock
Signing in only authenticates *who someone is*. It does not, by itself,
grant access to any space's content:
> [!IMPORTANT]
> **Reading and writing follow the linked Git account, not the sign-in
> method.** A person who signs in but never connects a Forgejo or GitHub
> account sees no spaces at all — not because f451 hides them, but because
> there is no permission to check against.
See [[spaces-and-git-providers]] for how a space is tied to a repository,
and the User Guide's *Getting started* page for what connecting an account
looks like for the person doing it.
## One person, several identities
Because each sign-in method produces its own identity, a person who signs
in once through Entra and once through GitHub becomes **two** separate
f451 users, each with its own set of connected Git accounts — see
[[github-sign-in]] for the practical consequence of that.

View file

@ -0,0 +1,75 @@
---
id: spaces-and-git-providers
title: Spaces and Git providers
description: Configuring which repositories are wiki spaces, service tokens, webhooks, and how the index stays current.
tags: [admin, spaces, git]
lang: en
---
# Spaces and Git providers
A space is one repository on Forgejo or GitHub, declared to f451 as one
entry in `F451_SPACES` — a JSON array on the `api` service:
```json
[
{
"id": "handbook",
"name": "Handbook",
"provider": "forgejo",
"owner": "docs",
"repo": "handbook",
"defaultLang": "en"
}
]
```
Without `F451_SPACES` set, the API starts in a minimal mode: only
`/healthz` and `/readyz` respond, and no space is visible at all.
## Service tokens are for indexing, not for user access
Each provider used by at least one space needs a service-account token:
| Variable | Used for |
|---|---|
| `F451_FORGEJO_URL` | Base URL of the Forgejo instance (no `/api/v1`). Also the one source for the service-account registry, the write-permission check, and — if Forgejo doubles as identity provider — the OIDC issuer. |
| `F451_FORGEJO_TOKEN` | Service-account token Forgejo spaces are indexed with. |
| `F451_GITHUB_TOKEN` | Service-account token GitHub spaces are indexed with. |
> [!NOTE]
> These tokens exist purely so the indexer can *read* repository content to
> build the search index and page tree. They are never used to write on a
> user's behalf — every write is committed with the writer's own linked
> provider token (see [[sign-in-options]]). A misconfigured or missing
> service token breaks indexing for that provider's spaces, not writing.
## Webhooks keep the index current
Configure a webhook in each space's repository pointing at f451, and set a
matching secret on the `api` service so f451 can verify it:
| Variable | Effect |
|---|---|
| `F451_WEBHOOK_SECRET_FORGEJO` | HMAC secret for Forgejo webhooks. Must match the secret entered when creating the webhook in the Forgejo repository. |
| `F451_WEBHOOK_SECRET_GITHUB` | Same, for GitHub webhooks. |
Without the matching secret set, every webhook signature check fails
(`401`) and pushes never trigger an incremental reindex — see
[[operations]] for how that shows up and how it self-heals.
## The drift job is the safety net
A background job compares each space's current `main` HEAD against the
last fully indexed commit every 5 minutes, and triggers a full reindex on
a mismatch. This means a webhook that never arrives — a firewall rule, a
wrong secret, a Git provider outage — is not a lasting problem: the index
catches up on its own within 5 minutes, no manual action required. See
[[operations]] for how to observe this happening.
## Templates across spaces
`F451_GLOBAL_TEMPLATES` optionally names one more repository
(`{"provider","owner","repo"}`, same shape as an `F451_SPACES` entry) that
supplies templates available to every space, in addition to a space's own
`_templates/*.md`.