Configuration

Configuration keys

Every admin.* configuration key:

KeyTypeDefaultDescription
admin.enabledboolfalseWhether the Accent Author admin shell is mounted at runtime.
admin.tokenOption<String>NoneAdmin token required for /_admin/reload and the admin shell login flow.
admin.brand_nameOption<String>NoneOptional brand name shown on the login card and top bar (falls back to “Accent Author” when unset).
admin.brand_logoOption<String>NoneOptional brand logo URL shown on the login card and top bar, resolved as a normal asset URL.
admin.dev_assets_pathOption<PathBuf>NoneOptional override that makes the admin shell read templates and static assets from disk instead of the embedded binary snapshot.
admin.audit_log_pathOption<PathBuf>NoneOptional override for the audit log path where successful page writes are appended as JSONL lines.
admin.mediaAdminMediaConfig(defaults below)Media upload settings: size limits, allowed types, and folder pattern.
admin.media.max_upload_mbu6420Maximum file size in megabytes per uploaded file.
admin.media.allowed_mime_prefixesVec<String>["image/", "application/pdf", "video/mp4", "audio/mpeg"]MIME-type prefixes the upload endpoint accepts.
admin.media.upload_folder_patternString"uploads/{YYYY}-{MM}/"Folder pattern for uploaded files relative to the content root, with UTC date replacement.

The admin token may also be set via the ACCENT_ADMIN_TOKEN environment variable, which takes precedence over admin.token. A variable or admin.token that is empty or only whitespace counts as unset. With no token set, the login form refuses every attempt, and in production POST /_admin/reload answers 503.

So does an unexpanded placeholder. Writing token: "${ACCENT_ADMIN_TOKEN}" keeps the credential out of the config file, and when the variable is not set the placeholder stays literal: that literal text counts as unset rather than becoming the token, so nobody can log in by typing what the config says. The login page and the startup log both name the variable to set. Prefer this to a literal token in any config file you commit, and note that ${VAR:-default} is not the same thing, because the default expands into a real credential.

Audit log

Every successful mutation appends one line to an append-only JSONL audit log (one JSON object per line) with a stable field order: ts (RFC3339 UTC), actor, action, path, an optional fields array (the frontmatter fields that changed, omitted when empty), and optional from / to for workflow transitions.

  • Location — when admin.audit_log_path is unset, the log defaults to <content_dir>/.admin/audit.log. When the key is set, its value is honored verbatim. The containing directory (.admin/) is created on demand, and the starter templates’ .gitignore excludes .admin/ from version control by default.
  • Actions — page edits record update_page (with the changed-field names); workflow transitions record workflow_transition (with from / to); page operations record create_page, move_page, and delete_page; media operations record media_upload, media_rename (with reference count and old/new URLs), and media_delete.
  • Best-effort writes — audit logging never breaks a save. The on-disk content write is committed first; if opening or writing the audit file fails, the system logs a warning and discards the audit line. A read-only audit volume cannot block the save path.

Conflict resolution

A conflict is detected when the __mtime recorded at the time the editor form was opened no longer matches the file’s current on-disk mtime — for example, because another author, a CLI tool, or a git pull changed the file in the meantime. When this happens the save handler returns 409 with no write, and renders a conflict modal.

The modal uses role="alertdialog" so assistive technologies announce it. It displays the page URL and the current on-disk mtime as metadata, and offers two choices:

  • Reload — discards your pending edits and fetches the latest version via an hx-get to /_admin/pages{page_url}, which returns the editor form with the fresh content and an updated mtime.
  • Force save — overwrites the on-disk changes. The button first updates the hidden mtime input to the fresh on-disk value, then submits the form, so the follow-up POST carries the current mtime and succeeds.

Mobile usage

The admin shell is responsive and usable on phones and small laptops. The editor adapts its layout to the viewport: wider screens place the form and live preview side by side, while narrower screens stack them into a single column and move the preview into a summonable overlay so the form has full width. Touch interactions follow the same actions as the desktop pointer flow — tap a row to open it, tap a thumbnail to open the media detail drawer, and use the modal buttons for move, delete, and conflict resolution. The mobile preview uses the same preview document as the desktop side pane.