Security Model

Demarkus is a versioned markdown document server. This page describes the attack surface, threat model, and hardening options for production deployments.

Attack Surface

The server is deliberately minimal:

Authentication

Write operations (PUBLISH, APPEND, ARCHIVE) require a capability token: a SHA-256 hash scoped to specific paths and to an access class (read or write).

Without a valid token, write requests are rejected. Without read tokens configured, all content is public (the default for public servers).

What a Compromised Token Gets You

If an attacker obtains a write token, they can:

Retention is the store’s only destructive operation. It needs no extra capability, since it runs under the same write authorization that could archive the document, so it is constrained instead: deletion targets come from enumerating the document’s own stored versions, never from request input; every removal resolves inside an os.Root anchored at the store root, so a planted symlink cannot redirect it; and each pruned range is audit-logged with the writer’s token label. Clients warn before setting retention on a document that is not a generated artifact.

Mitigation:

What a Compromised Server Gets You

If an attacker gains control of the server process, they can:

With systemd hardening (see below), they cannot:

Systemd Hardening

For production Linux deployments, the install script automatically adds these directives to the systemd unit:

[Service]
ProtectSystem=strict
ReadWritePaths=/srv/site
PrivateTmp=yes
NoNewPrivileges=yes
ProtectHome=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictNamespaces=yes
RestrictSUIDSGID=yes

ProtectSystem=strict makes the entire filesystem read-only. ReadWritePaths grants an exception for the content directory only. The kernel enforces these restrictions, with no application changes needed.

Set ReadWritePaths to match your DEMARKUS_ROOT. The server only reads the tokens file and TLS certificates, so paths like /etc/demarkus don’t need write access; ProtectSystem=strict already allows reads.

Verify hardening is active:

systemctl show demarkus -p ProtectSystem,ProtectHome,NoNewPrivileges,ReadWritePaths

Existing Installs

Running demarkus-install update detects if the systemd unit is missing hardening and prompts you to apply it. If the service fails to start after hardening, it automatically rolls back to the previous config.

Read-Only Mode (Maximum Lockdown)

For Gemini-level security, run the server in read-only mode. All PUBLISH, APPEND, and ARCHIVE requests are rejected, and the server needs zero write access to the filesystem.

Quick setup

demarkus-server -read-only -root /srv/site

Or via environment variable:

DEMARKUS_READ_ONLY=1 demarkus-server -root /srv/site

Chroot install

For maximum isolation, use the read-only install script. It runs the server inside a chroot with a fully read-only filesystem:

curl -fsSL https://raw.githubusercontent.com/latebit-io/demarkus/main/install-readonly.sh | sudo bash -s -- --domain example.com

The chroot structure:

/srv/demarkus/
  bin/demarkus-server    <- binary inside chroot
  content/               <- documents
  tls/cert.pem           <- certificates
  tls/key.pem

The systemd unit uses RootDirectory and ReadOnlyPaths=/, so the process cannot see or write anything outside the chroot.

Publishing content locally

Publish with demarkus-publish. It writes directly to the versioned store on disk, bypassing the server:

demarkus-publish -root /srv/demarkus/content -path /index.md -body "# Hello"
echo "# Hello" | demarkus-publish -root /srv/demarkus/content -path /index.md

Full versioning is preserved: demarkus-publish uses the same store code as the server. The server just serves what’s on disk.

Knowledge system (broker)

In a knowledge system the broker is the only public surface and worlds stay private behind it.

Comparison

  SSH Web + CGI Gemini Demarkus
Code execution Shell access Scripts/templates None None
Database Full access Often None None
Write scope Entire machine Varies None (read-only) Content dir only
Auth model Keys / passwords Sessions / cookies Client certs Capability tokens
Worst case Full compromise RCE, data breach DoS Markdown overwrite