milestone 32: task-shaped configuration, file mode as a rendering, config status api
Gates / frontend (push) Successful in 1m22s
Gates / test (push) Successful in 1m54s
Gates / test-aarch64 (push) Successful in 7m57s
Gates / package (push) Successful in 5m29s
Gates / container (push) Successful in 10s
CI / gates (push) Successful in 32m51s

This commit is contained in:
2026-08-22 22:42:50 +02:00
parent 025edbb093
commit 7e0df5fd94
89 changed files with 6101 additions and 3750 deletions
+41 -14
View File
@@ -35,8 +35,8 @@ info:
envelope, naming the file. Operations that change runtime state —
`/api/pause`, `POST /api/blocklists/update`, `/api/certs/reload`, the
login and the logout — stay live, as does `DELETE /api/clients/{id}`
for a client the file does not declare. `GET /api/settings` reports
the live authority, so a client reads the mode rather than
for a client the file does not declare. `GET /api/config/status`
reports the live authority, so a client reads the mode rather than
discovering it from a rejection.
servers:
@@ -1753,6 +1753,26 @@ paths:
"503":
$ref: "#/components/responses/Unavailable"
/api/config/status:
get:
summary: Read the configuration authority and restart state
description: |
Which source governs this process's configuration, and whether a
change already written waits for a restart. Both are per-process
facts the database cannot answer, and this is their one home: a
client reads the mode here rather than discovering it from a 403.
responses:
"200":
description: The live authority and the pending-restart flag.
content:
application/json:
schema:
$ref: "#/components/schemas/ConfigStatus"
"401":
$ref: "#/components/responses/Unauthorized"
"429":
$ref: "#/components/responses/RateLimited"
/api/certs/reload:
post:
summary: Reload the TLS certificates from disk
@@ -2910,7 +2930,7 @@ components:
SettingsEnvelope:
type: object
required: [settings, restart_required, authority]
required: [settings, restart_required]
properties:
settings:
$ref: "#/components/schemas/Settings"
@@ -2919,21 +2939,21 @@ components:
items: { type: string }
description: |
Every `section.field` key that needs a restart to take effect —
currently all of them.
authority:
$ref: "#/components/schemas/Authority"
currently all of them. Whether a restart is *owed* right now is
process state, and lives on `/api/config/status`.
Authority:
ConfigStatus:
type: object
description: |
Which source governs this process's configuration. This is how a
client learns that configuration is read-only; it never has to probe
a write route for a 403. The block rides this authenticated endpoint
because `path` is a filesystem path, and never appears on the open
`/api/version` or `/api/health`.
required: [mode, path, reconciled_at]
Which source governs this process's configuration, and whether a
committed change waits for a restart. This is how a client learns
that configuration is read-only; it never has to probe a write route
for a 403. It rides an authenticated endpoint because `path` is a
filesystem path, and never appears on the open `/api/version` or
`/api/health`.
required: [authority, path, reconciled_at, restart_pending]
properties:
mode:
authority:
type: string
enum: [database, managed_file]
description: |
@@ -2955,6 +2975,13 @@ components:
a stepped clock or a preserved mtime defeats the comparison
either way, and `nxdns import` can move the database without
moving either timestamp.
restart_pending:
type: boolean
description: |
True once this process has committed a configuration change that
takes effect only at the next start — an upstream write or a
settings key. Nothing clears it but process exit, and it is
never persisted, so a false after a restart is the truth.
SettingsPatch:
type: object