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.

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 path uses the same POST /_admin/preview-render endpoint as the desktop side pane.