Files

143 lines
9.0 KiB
Markdown

# Xiteng Site component labels
`xiteng.site` does not contain a hardcoded service list. A Docker container opts one or more
components into the public catalog with labels under this namespace:
```yaml
labels:
- "xiteng.site.component.example.enabled=true"
- "xiteng.site.component.example.name=Example"
- "xiteng.site.component.example.description=What this component does."
- "xiteng.site.component.example.section=services"
- "xiteng.site.component.example.category=开发与协作"
- "xiteng.site.component.example.url=https://example.xiteng.site"
- "xiteng.site.component.example.endpoint=example.xiteng.site:443"
- "xiteng.site.component.example.access=sso"
- "xiteng.site.component.example.access-label=需要 Authentik"
- "xiteng.site.component.example.icon=EX"
- "xiteng.site.component.example.icon-url=https://example.xiteng.site/favicon.svg"
- "xiteng.site.component.example.accent=blue"
- "xiteng.site.component.example.order=100"
- "xiteng.site.component.example.navigation=new-tab"
- "xiteng.site.component.example.portal-link=native"
- "xiteng.site.component.example.monitor.enabled=true"
- "xiteng.site.component.example.monitor.url=http://example:8080/healthz"
```
## Schema
| Field | Required | Values / behavior |
|---|---:|---|
| `enabled` | yes | Only the exact value `true` publishes the component. |
| `name` | yes | Public display name. |
| `description` | recommended | Public description; never put secrets here. |
| `section` | yes | `services` or `infrastructure`. |
| `category` | recommended | Dynamic group heading. |
| `url` | no | Only HTTP(S) URLs are accepted. No URL renders a non-clickable card. |
| `endpoint` | no | Public protocol endpoint or connection hint. |
| `access` | yes | Machine-readable mode such as `public`, `sso`, `mixed`, `access-key`, `ssh-key`, `local`, or `internal`. |
| `access-label` | recommended | Human-readable access boundary shown on the card. |
| `icon` | no | Final fallback text or emoji, limited to eight characters. |
| `icon-url` | no | Preferred HTTP(S) icon URL. Without it, the UI tries `/favicon.svg`, `/favicon.ico`, then `/favicon.png` on the component origin before showing `icon`. |
| `accent` | no | `red`, `green`, `yellow`, `blue`, or `ink`. |
| `order` | no | Numeric order inside a section; defaults to `999`. |
| `navigation` | no | `new-tab` (default for URLs), `same-tab`, or `endpoint` (default without a URL). |
| `portal-link` | no | `embedded`, `native`, or `none`; documents how the service returns to the Portal. |
| `monitor.enabled` | no | `true` enables the built-in HTTP GET probe. |
| `monitor.url` | when enabled | Internal HTTP(S) target. It is never returned by the public API. |
| `monitor.interval` | no | Check interval in seconds, default `60`. |
| `monitor.failures` | no | Consecutive failures before `down`, default `3`. |
| `monitor.timeout` | no | Request timeout in seconds, default `10`. |
| `monitor.accept` | no | Accepted HTTP codes, default `200-299`; comma-separated values and ranges are supported. |
The component id (`example` above) must be globally stable. One container may publish multiple
components by using multiple ids, which is useful for services such as SeaweedFS Web and its S3 API.
The registry reads container state and image names from Docker. It returns only the public fields above plus
Compose project/service names, runtime status, sanitized monitor state, response time, last check time, and
24-hour availability. Internal monitor URLs, errors, environment variables, mounts, raw labels, Docker
configuration, and secret values are never returned to the public site container.
## Navigation contract
Components with a public HTTP(S) URL open in a new tab by default with `noopener noreferrer`, leaving the
Portal available in the original tab. Owned or officially customizable applications link back to
`https://xiteng.site/?focus=<component-id>#services`; the Portal clears incompatible filters, scrolls to the
component card, briefly highlights it, and then removes the `focus` query parameter. Protocol endpoints and
internal-only components remain non-clickable. Do not inject navigation into third-party HTML at the proxy.
## Monitoring and lifecycle
The Registry is the only discovery and lifecycle control plane. It uses the stable component id as the
database primary key, so a component can never create multiple monitors. It performs bounded-concurrency
HTTP GET probes and stores state in `site/data/registry.db`:
```text
active → missing → archived → purged
```
- `active`: the component Label is currently present on a Docker container;
- `missing`: the container or Label disappeared, but catalog and monitor history remain for 30 days;
- `archived`: monitoring is paused and the component is hidden from the default public catalog;
- `purged`: an administrator explicitly removes the component and all of its monitoring data.
A component that returns with the same id before purge reuses its existing history. Raw checks are retained
for 30 days; hourly and daily aggregates are retained for 365 days. Response bodies are never stored.
`https://xiteng.site/admin` and `/api/admin/*` are protected by Authentik ForwardAuth and additionally
require the exact username `liooil`; this check is repeated in the site backend and Key Vault. All other
users use `/account` and `/api/account/*`. The account page is the canonical self-service surface for
profile name/email, avatar resolution, password recovery, TOTP, Passkeys, Authentik sessions, and Provider
configuration constrained by the session's `(issuer, sub)` pair. Provider definition, model list, and an
optional credential are saved from one form. Backend credentials are encrypted by Key Vault; Frontend
credentials remain in the current browser's IndexedDB. Either mode can be saved and connectivity-tested in
the same action. The public homepage remains unauthenticated.
The admin page is the canonical control plane for human users, ordinary groups, password setup/recovery,
session revocation, authenticator status/reset, and a group-only application access matrix. The native
Authentik admin UI is hidden. TOTP and Passkey enrollment launches dedicated Authentik setup flows and
returns to `/account`; list, rename, delete, reset, and ownership checks remain in the Portal API. The account
page hashes the normalized email with SHA-256 in the browser, then loads Gravatar and Libravatar directly with
no referrer; a deterministic initials image remains visible while loading and on failure. Avatar bytes never
pass through the Portal backend. The Portal accesses Authentik with a server-only API token and
records every identity mutation in a local JSONL audit; password values are never logged. `liuhome` is
protected and currently contains `liooil` and `ziyue`; all managed non-public applications are restricted
to that group.
The default Authentik identification stage enables WebAuthn conditional UI. A discoverable Passkey can
authenticate directly on `auth.xiteng.site`; Authentik's default flow policies then skip both password and
the later MFA stage. Username/password plus TOTP remains available as a fallback, and new Passkeys are
enrolled with the default `resident_key_requirement=preferred` setup stage.
## Host metrics
The internal `metrics` service publishes the sanitized `/api/metrics` payload used by the device status
cards. It reads host CPU and memory counters from a read-only `/proc` mount, root filesystem capacity
through a read-only bind on the same filesystem, and NVIDIA GPU telemetry through the `utility` driver
capability. The public payload is limited to:
- CPU usage, model, logical core count, and load averages;
- memory and root filesystem used/available/total values;
- GPU model, utilization, VRAM, temperature, and power;
- hostname and collection timestamp.
The metrics container has no Docker socket and no public router. The site server proxies its fixed
internal endpoint as `/api/metrics`; arbitrary host files and commands are not exposed.
## PWA and icons
`favicon.svg` is the source artwork for browser and install icons. The PNG and ICO derivatives live in
`icons/` and at `favicon.ico`. `manifest.webmanifest` enables standalone installation and shortcuts to the
service and infrastructure catalogs. `sw.js` caches only the public page shell and static artwork. Runtime
component/metrics APIs, Authentik paths, and all administration requests always use the network and are
never written to the PWA cache.
Setting `enabled=false`, removing the labels, or removing the container moves the component to `missing`
without changing `index.html` or `app.js`. The old `homepage.*` and `kuma.*` namespaces are no longer read;
new and existing components use only the `xiteng.site.component.*` schema.
Static edge caching is a separate, security-sensitive declaration and is intentionally not part of
the public component schema. A service can opt public, user-independent asset directories into the
shared cache with `xiteng.site.cache.<policy>.*` labels. See `edge-cache/README.md` for the schema;
never apply it to API, admin, callback, tokenized download, HTML, or user-content paths.