No description
  • Go 46.1%
  • JavaScript 31.5%
  • CSS 10.9%
  • HTML 5%
  • Shell 4.3%
  • Other 2.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-11-06 18:05:19 +00:00
bin Security patches installed 2026-11-05 21:26:52 +00:00
cmd Security patches installed 2026-11-05 21:26:52 +00:00
data Security patches installed 2026-11-06 18:05:19 +00:00
internal Security patches installed 2026-11-05 21:26:52 +00:00
scripts Updated the settings menu 2026-11-03 16:55:37 +00:00
.gitignore Add gitignore for Go project: build outputs, runtime data, secrets, and editor artifacts 2026-10-31 21:42:50 +00:00
go.mod Security patches installed 2026-11-04 16:42:23 +00:00
go.sum Security patches installed 2026-11-04 16:42:23 +00:00
install.sh Security patches installed 2026-11-05 21:26:52 +00:00
Makefile Updated the settings menu 2026-11-03 16:55:37 +00:00
README.md Security patches installed 2026-11-05 21:26:52 +00:00
to.txt Updated the settings menu 2026-11-03 16:55:37 +00:00
uninstall.sh Updated the settings menu 2026-11-03 16:55:37 +00:00

Documentation Center (Go)

A self-hosted, single-binary IT documentation platform. It keeps organizations, asset inventory, flexible records, runbooks, and encrypted credentials in one searchable workspace.

The application is written in Go, stores data in SQLite, and serves a server-rendered SPA from the same binary. There are no required external services, Node.js builds, or CGO dependencies. Duo Push is optional.

Features

  • Organizations — client/company records with contact and address details, plus a consolidated view of every related record.
  • Configurations — documented servers, workstations, network devices, cloud services, and other inventory. Includes type, status, serial, model, IP/hostname, warranty dates, and notes.
  • Flexible assets — admin-defined asset types with custom fields (text, textarea, number, date, checkbox, select, URL, email). Ships with Contacts, Vendors, Applications, Locations, and Network segments.
  • Documents — Markdown overviews plus structured sections: text, steps, images, checklists, callouts, code, tables, key-value details, and links. Includes live preview and safe rendering.
  • Passwords — AES-256-GCM encrypted secrets, optional authenticator (TOTP) keys that generate rotating 2FA codes, category filters, reveal-on-demand, and role-based access. Secrets are never included in list responses.
  • Relationships — link configurations, flexible assets, and documents with typed relationships.
  • Organization scope — pick an organization in the top bar to narrow the dashboard, inventory, documents, passwords, activity, and search to that client. The selection persists across pages and reloads; use Show all organizations to clear it.
  • Global search — search across organizations, configurations, flexible assets, documents, and password metadata. Cmd/Ctrl+K opens quick search. Search follows the active organization scope.
  • Activity log — audit trail for sign-ins, creates, updates, deletes, and token changes.
  • Users and roles — admin, editor, and viewer roles with server-side enforcement.
  • API tokens — hashed bearer tokens for scripts and integrations, shown once at creation.
  • Two-factor authentication — enroll an authenticator app (TOTP), Duo Push, or both. Optional per user; enforced at sign-in.
  • Installable app (PWA) — install Documentation Center from the browser on desktop, Android, and iOS. The installed app opens in its own window and keeps the interface available offline.
  • Dashboard — inventory totals, expiring warranties, recent activity, and quick links.
  • Demo workspace — optional sample data for trying the product immediately.

Quick start

Requirements: Go 1.26 or newer. The module uses the pure-Go modernc.org/sqlite driver, so no C compiler is required.

git clone <your-repository> documentation-center
cd documentation-center

# Run with the built-in demo data.
make run

Open http://localhost:8080.

On first run the server creates the administrator and prints a generated password once, at the end of its startup output:

Username: admin
Password: <generated>

Use it to sign in, then change it. To choose the password yourself, set DOCUMENTATION_CENTER_ADMIN_PASSWORD before starting. The first administrator is only created when the database has no users. Demo data is loaded only into an empty database.

To build a production binary:

make build
./bin/documentation-center

Without make:

go run ./cmd/documentation-center
go build -trimpath -o documentation-center ./cmd/documentation-center

Install as an app (PWA)

Documentation Center is a Progressive Web App. The web interface can be installed like a native application while continuing to use the same self-hosted server and SQLite database. The manifest, icons, and offline shell are embedded in the binary.

Service workers and installation require a secure context: use HTTPS in production, or http://localhost during development. Android Chrome will not install the app from a plain-HTTP LAN address such as http://192.168.1.10:8080; put the server behind HTTPS for that. Installation is available after signing in. When the browser offers it, an Install app button also appears in the sidebar (on iOS, use Share → Add to Home Screen).

Platform How to install
Chrome / Edge (desktop) Open the app, then use the install icon in the address bar or Menu → Install Documentation Center.
Chrome (Android) Open the app, then Menu → Add to Home screen → Install.
Safari (iOS / iPadOS) Open the app, tap Share → Add to Home Screen.
Safari (macOS) File → Add to Dock.

The installed app keeps the interface shell cached so it opens instantly and shows an offline screen when the server is unreachable. Records, credentials, and search always come from the server and are never cached on the device; an active connection is required to view or edit data. API responses are explicitly excluded from the service worker cache.

To update the installed app, deploy the new binary and reload it while online. The service worker picks up the new shell on the next launch.

Desktop client (Linux AppImage)

Documentation Center ships a native desktop client: a GTK 3 window with an embedded WebKitGTK view. On first launch it asks whether the client should connect to a remote Documentation Center server or run a private workspace on this computer. The choice is remembered, and you can change it later from the window menu (Ctrl+Shift+S) or from the "Change workspace" link on every sign-in screen.

Build the client and package it as a portable AppImage:

make appimage
# -> bin/Documentation-Center-<version>-<arch>.AppImage

Building requires Go, a C compiler, appimagetool, mksquashfs (from squashfs-tools), and the GTK 3 + WebKitGTK 4.1 development packages (Debian/Ubuntu: libgtk-3-dev libwebkit2gtk-4.1-dev). Run the AppImage directly; no installation is needed:

./bin/Documentation-Center-0.1.0-x86_64.AppImage

Command-line options:

# Connect to a remote server and remember it.
./Documentation-Center-0.1.0-x86_64.AppImage --server https://docs.example.com

# Run the bundled server locally.
./Documentation-Center-0.1.0-x86_64.AppImage --local

# Reopen the server picker.
./Documentation-Center-0.1.0-x86_64.AppImage --configure

The client stores its choice in $XDG_CONFIG_HOME/documentation-center/desktop.json. In local mode the bundled server keeps its SQLite database and encryption key in $XDG_DATA_HOME/documentation-center (usually ~/.local/share/documentation-center); override this with DOCUMENTATION_CENTER_DATA_DIR. WebKit's cookies and caches live in the same data directory and in $XDG_CACHE_HOME/documentation-center, so signed-in sessions survive restarts. Logs are written to desktop.log and documentation-center.log there. On first run, the generated administrator password is printed in documentation-center.log.

Runtime requirements: GTK 3 and WebKitGTK 4.1 (libwebkit2gtk-4.1-0 on Debian/Ubuntu, webkit2gtk4.1 on Fedora). If WebKitGTK 4.1 or a graphical display is unavailable, the AppImage falls back to starting the bundled server and opening the default browser.

Native install (systemd)

The install script builds the binary and installs it as a native systemd service. Run it as root for a system-wide service that starts at boot, or as a normal user for an unprivileged per-user service. The mode is detected from the current user; --system and --user override it.

System-wide install (root)

sudo ./install.sh
sudo ./install.sh --demo
sudo ./install.sh --addr 127.0.0.1:8080

By default the installer:

  • creates a dedicated documentation-center system account (use --service-user root to run as root instead);
  • installs the binary to /usr/local/bin/documentation-center;
  • writes /etc/documentation-center/documentation-center.env (mode 0600, owned by root);
  • stores the database and encryption key in /var/lib/documentation-center/, owned by the service account;
  • enables documentation-center.service in multi-user.target, so it starts at boot.
Item Path
Binary /usr/local/bin/documentation-center
Configuration /etc/documentation-center/documentation-center.env
Data and encryption key /var/lib/documentation-center/
Service unit /etc/systemd/system/documentation-center.service

The service runs as the documentation-center account with basic systemd sandboxing (ProtectSystem=full, PrivateTmp, NoNewPrivileges).

Manage it with:

sudo systemctl status documentation-center
sudo systemctl restart documentation-center
sudo journalctl -u documentation-center -f

Per-user install

For a per-user install without root:

./install.sh
./install.sh --demo
./install.sh --addr 127.0.0.1:8080
Item Path
Binary ~/.local/bin/documentation-center
Configuration ~/.config/documentation-center/documentation-center.env
Data and encryption key ~/.local/share/documentation-center/
Service unit ~/.config/systemd/user/documentation-center.service

Manage it with:

systemctl --user status documentation-center
systemctl --user restart documentation-center
journalctl --user -u documentation-center -f

The per-user service starts when you sign in, not at boot. To keep it running without logging in:

sudo loginctl enable-linger "$USER"

Install options

Option Description
--addr ADDR Listen address (default 0.0.0.0:8080).
--lan Explicitly listen on 0.0.0.0:8080 (the default).
--data-dir PATH Data directory (default /var/lib/documentation-center as root, ~/.local/share/documentation-center as a user).
--demo Seed demo content into an empty database.
--admin-username USER First administrator username.
--admin-name NAME First administrator display name.
--admin-password PW First administrator password (default: random).
--system Force a system-wide install (requires root).
--user Force a per-user install.
--service-user USER System service account (default documentation-center; use root to run as root).
--no-build Install the existing bin/documentation-center instead of building.

A generated administrator password is printed once and stored in the environment file (mode 0600). If the environment file already exists, it is left unchanged on re-install.

To change settings, edit /etc/documentation-center/documentation-center.env for a system install or ~/.config/documentation-center/documentation-center.env for a per-user install, then restart the service. Keep the file private and retain secret.key with the database; without it, stored password secrets cannot be recovered.

Resetting an existing account's password

DOCUMENTATION_CENTER_ADMIN_PASSWORD is only used when creating the very first user in an empty database. It cannot change an existing account's password, and it cannot recover a lost administrator password. To reset one, use the built-in command against the live data directory (the server may keep running):

# system-wide install
sudo documentation-center set-password --data-dir /var/lib/documentation-center --username admin --generate

# per-user install
documentation-center set-password --data-dir ~/.local/share/documentation-center --username admin --generate

The password can also be supplied with --password '…', read from standard input with --stdin, or taken from $DOCUMENTATION_CENTER_ADMIN_PASSWORD with no password flag. Every active session for the account is revoked, so all devices must sign in again. The command works against any user account, not only administrators.

sudo ./uninstall.sh              # system-wide: ask whether to keep the database and key
sudo ./uninstall.sh --keep-data  # keep data without asking
sudo ./uninstall.sh --purge      # delete data, with a confirmation prompt
sudo ./uninstall.sh --purge --yes --remove-user  # also delete the service account

./uninstall.sh                   # per-user uninstall

Omit sudo for a per-user install. When run interactively, the script asks whether to preserve the data directory. In a non-interactive session it keeps the data unless --purge is supplied. A system-wide uninstall stops and disables the service and removes the binary and configuration; any other running server process (for example one started by hand from bin/) is stopped too. The documentation-center service account is kept unless --remove-user is given.

By default the plain HTTP listener is bound to 0.0.0.0:8080, so it is reachable from other machines on your local network. Use it only on a trusted network. To restrict access to this machine, install with --addr 127.0.0.1:8080. For untrusted networks, place the app behind a TLS-terminating reverse proxy and set DOCUMENTATION_CENTER_TRUSTED_PROXIES=true; session cookies are marked Secure automatically when the proxy forwards X-Forwarded-Proto: https.

Configuration

All configuration is supplied through environment variables.

Variable Default Description
DOCUMENTATION_CENTER_ADDR :8080 HTTP listen address.
DOCUMENTATION_CENTER_DATA_DIR ./data Directory for the database and generated key.
DOCUMENTATION_CENTER_DB_PATH <data>/documentation-center.db SQLite database path.
DOCUMENTATION_CENTER_SECRET generated Passphrase used to derive the AES-256 encryption key. If empty, <data>/secret.key is created with mode 0600.
DOCUMENTATION_CENTER_DEMO true Seed demo organizations and documentation into an empty database. Set to false for a clean install.
DOCUMENTATION_CENTER_ADMIN_USERNAME admin Username for the initial administrator.
DOCUMENTATION_CENTER_ADMIN_NAME IT Administrator Display name for the initial administrator.
DOCUMENTATION_CENTER_ADMIN_PASSWORD generated Password for the initial administrator. Used only when creating the first user; if unset, a random password is generated and printed once.
DOCUMENTATION_CENTER_SESSION_TTL 72h Session lifetime; must be at least 1h.
DOCUMENTATION_CENTER_SECURE_COOKIES false Force the Secure flag on session cookies. Cookies are also marked Secure automatically when the request is HTTPS, including X-Forwarded-Proto: https from a reverse proxy.
DOCUMENTATION_CENTER_TRUSTED_PROXIES false Trust X-Forwarded-For/X-Real-IP from a reverse proxy. Enable only when the app is not directly reachable.
DOCUMENTATION_CENTER_DUO_IKEY empty Duo Auth API integration key. Set together with DOCUMENTATION_CENTER_DUO_SKEY and DOCUMENTATION_CENTER_DUO_API_HOST to enable Duo Push.
DOCUMENTATION_CENTER_DUO_SKEY empty Duo Auth API secret key. Keep this value private.
DOCUMENTATION_CENTER_DUO_API_HOST empty Duo API hostname, such as api-xxxxxxxx.duosecurity.com. Do not include a URL scheme.

Upgrading an existing database converts each account email to a username automatically. The part before @ is used, and collisions receive numeric suffixes such as admin-2. Existing sessions and API tokens continue to work. Usernames are case-insensitive.

Reverse proxy and TLS

Run Documentation Center behind a TLS-terminating reverse proxy such as Caddy, nginx, or Traefik. Cookies are marked Secure automatically when the proxy sends X-Forwarded-Proto: https; set DOCUMENTATION_CENTER_SECURE_COOKIES=true to force it if your proxy does not. If the proxy forwards client IPs, set DOCUMENTATION_CENTER_TRUSTED_PROXIES=true. When the proxy runs on the same host, install with --addr 127.0.0.1:8080 so only the proxy can reach the app directly.

Example Caddyfile:

documentation-center.example.com {
    reverse_proxy 127.0.0.1:8080
}

Roles

Role Read Create/edit/delete content Reveal passwords Manage users, asset types, and all tokens
viewer Yes No No No
editor Yes Yes Yes No
admin Yes Yes Yes Yes

Admins can create users, define flexible asset types, view all API tokens, and revoke any token. Editors can manage content and their own API tokens. Viewers have read-only access and see password records without their secret values.

Two-factor authentication

Multi-factor authentication is optional per user. An account can enable an authenticator app (TOTP, RFC 6238), Duo Push, or both. When both are enabled, signing in succeeds with either method.

Authenticator app (TOTP)

  • Users enable TOTP from Security → Two-factor authentication in the web app, or through the API (POST /api/v1/auth/totp/setup, POST /api/v1/auth/totp/confirm).
  • Enrollment returns a secret and an otpauth:// provisioning URI that can be scanned into Google Authenticator, Authy, 1Password, Bitwarden, or any other TOTP app.
  • Confirmation requires the current account password; disabling also requires it.
  • At sign-in, after the password checks out, the totp code from the login request is verified. A missing or invalid code returns 401 with requires_totp: true; a valid code issues the session.
  • Secrets are generated per user, encrypted at rest, and verified with a ±1 time-window tolerance for clock drift. Failed attempts count against the login rate limit.

Duo Push

Duo Push uses a Duo Auth API application, so authentication stays inside this app: the server sends the push and waits for the result.

  1. In the Duo Admin Panel, create an Auth API application and copy its integration key, secret key, and API hostname.
  2. Set DOCUMENTATION_CENTER_DUO_IKEY, DOCUMENTATION_CENTER_DUO_SKEY, and DOCUMENTATION_CENTER_DUO_API_HOST. All three are required; startup fails if only some are set.
  3. Restart the server. Security → Two-factor authentication now offers both methods.
  4. The user chooses Duo Push, enters their Duo username (defaults to their Documentation Center username), and confirms with their account password. The server sends a real push before enabling MFA, so a bad username cannot lock the account out at the next sign-in.

At sign-in, the web form first submits the username and password. When the account uses Duo, the server returns 401 with requires_duo: true without contacting Duo. The form automatically retries with "duo": true, which sends the push and waits up to about 60 seconds. API clients should make the same two-step request. A denied or timed-out push returns 401 with requires_duo: true and counts against the login rate limit.

Duo credentials stay in the server environment. Only the resolved Duo username is stored with the user record. If the user record has no Duo username, the Documentation Center username is used. API tokens authenticate without an interactive MFA challenge (machine-to-machine access), and bearer-token requests are not challenged.

Authenticator codes stored with passwords

Password records can also hold a TOTP shared secret for the account they describe, so the workspace can generate the same rotating 6-digit codes as Google Authenticator, Authy, 1Password, or any other authenticator app.

  • Add a secret from the password editor: paste either the base32 setup key or the full otpauth://totp/... provisioning URI. Issuer, account name, algorithm, digits, and period are read from the URI when present.
  • The secret is encrypted at rest with the same AES-256-GCM key as passwords and is never included in list or detail responses.
  • Reveal a password to see its username, password, and current authenticator code together in one view, with a countdown and copy button. The password is shown when you press the eye button; the authenticator code starts masked and can be toggled with its own eye control. Codes rotate automatically while the detail view is open.
  • Opening a password detail page does not decrypt or log anything. Pressing the eye button decrypts the credential and writes a reveal to the activity log; using the copy button also decrypts and is logged. The background countdown refresh does not add another audit entry every 30 seconds.
  • Editors and admins can manage authenticator secrets; viewers cannot reveal them.

From the Passwords list, editors and admins can click anywhere on a credential row to open its detail view in the current tab, showing the username, password, and authenticator code together, with the same masking, copy, and rotating countdown. Edit and delete actions are available on the detail view rather than the list. The secret stays hidden until the eye button is pressed; the copy button is available while masked, and both actions are written to the activity trail. Editors and admins can also manage authenticator secrets; viewers cannot reveal them.

REST API

All API endpoints live under /api/v1. Authentication uses either a session cookie or a bearer token.

Session authentication

Cookie-authenticated requests that change state must include X-Requested-With: documentation-center. This is a CSRF defense. Bearer-token requests are exempt.

# Sign in and save the session cookie.
curl -c cookies.txt \
  -H 'Content-Type: application/json' \
  -H 'X-Requested-With: documentation-center' \
  -d '{"username":"admin","password":"<initial administrator password>"}' \
  http://localhost:8080/api/v1/auth/login

# Use the session.
curl -b cookies.txt http://localhost:8080/api/v1/organizations

API tokens

Create a token in Administration → API tokens. The raw token is shown once. Store it in a secret manager.

# Create an organization with a bearer token.
curl -H 'Authorization: Bearer itg_...' \
  -H 'Content-Type: application/json' \
  -d '{"name":"Acme Corp","short_name":"ACME"}' \
  http://localhost:8080/api/v1/organizations

Tokens are stored as SHA-256 hashes, not plaintext. Revoking a token immediately invalidates it.

Endpoints

Method Path Role Description
POST /api/v1/auth/login public Sign in and create a session. Accepts an optional totp code for TOTP accounts or duo: true for Duo accounts; returns requires_totp or requires_duo when a valid password needs a second factor.
POST /api/v1/auth/logout any End the current session.
GET /api/v1/auth/me any Current user, including mfa_enabled, mfa_provider, and duo_username.
POST /api/v1/auth/totp/setup any Start TOTP enrollment. Returns a secret and otpauth_url for the authenticator app.
POST /api/v1/auth/totp/confirm any Confirm a TOTP code and enable two-factor authentication. Accepts code and password.
POST /api/v1/auth/duo/setup any Enroll in Duo Push. Accepts duo_username and password, sends a test push, and enables Duo only after approval.
POST /api/v1/auth/mfa/disable any Disable the active two-factor method. Accepts password. /api/v1/auth/totp/disable remains an alias.
GET /api/v1/meta any Configuration types, statuses, categories, roles, field types, relationship types, MFA providers, and duo_available.
GET /api/v1/dashboard any Dashboard summary. Accepts optional organization_id.
GET /api/v1/search?q= any Global search. Accepts optional organization_id.
GET /api/v1/activity any Activity feed. Supports organization_id, entity_type, entity_id, limit, offset.
GET /api/v1/organizations any List organizations. Supports q, limit, offset.
POST /api/v1/organizations editor Create an organization.
GET /api/v1/organizations/{id} any Get an organization.
PATCH /api/v1/organizations/{id} editor Update an organization.
DELETE /api/v1/organizations/{id} editor Delete an organization and related records.
GET /api/v1/configurations any List configurations. Supports organization_id, type, status, q, limit, offset.
POST /api/v1/configurations editor Create a configuration.
GET /api/v1/configurations/{id} any Get a configuration.
PATCH /api/v1/configurations/{id} editor Update a configuration.
DELETE /api/v1/configurations/{id} editor Delete a configuration.
GET /api/v1/flexible-asset-types any List flexible asset types.
POST /api/v1/flexible-asset-types admin Create a flexible asset type.
GET /api/v1/flexible-asset-types/{id} any Get a type and its field definitions.
PATCH /api/v1/flexible-asset-types/{id} admin Update a type.
DELETE /api/v1/flexible-asset-types/{id} admin Delete a type that has no records.
GET /api/v1/flexible-assets any List flexible assets. Supports organization_id, type_id, q, limit, offset.
POST /api/v1/flexible-assets editor Create a flexible asset.
GET /api/v1/flexible-assets/{id} any Get a flexible asset.
PATCH /api/v1/flexible-assets/{id} editor Update a flexible asset.
DELETE /api/v1/flexible-assets/{id} editor Delete a flexible asset.
GET /api/v1/documents any List documents. Supports organization_id, q, limit, offset.
POST /api/v1/documents editor Create a document. Accepts content (Markdown) and an optional sections array.
GET /api/v1/documents/{id} any Get a document, including its structured sections.
PATCH /api/v1/documents/{id} editor Update a document, including its sections.
DELETE /api/v1/documents/{id} editor Delete a document.
POST /api/v1/documents/images editor Upload an image for an images section. Send multipart form data with an image field (PNG, JPEG, GIF, or WebP; maximum 12 MB).
GET /api/v1/documents/images/{name} any Fetch an uploaded document image.
GET /api/v1/passwords any List password metadata. Supports organization_id, category, q, limit, offset. Includes a has_totp flag.
GET /api/v1/passwords/{id} any Get a password record (metadata only; use /reveal for the secret).
POST /api/v1/passwords editor Create a password. Accepts totp_secret (base32 key or otpauth:// URI). A password may be omitted when an authenticator secret is supplied.
POST /api/v1/passwords/{id}/reveal editor Decrypt and return a password value, notes, and the current authenticator code when configured. This is logged.
POST /api/v1/passwords/{id}/totp editor Return the current authenticator code and countdown. Access is recorded in the activity log.
GET /api/v1/passwords/{id}/totp editor Return the current authenticator code without a new audit entry, for countdown refreshes.
PATCH /api/v1/passwords/{id} editor Update a password. Send totp_secret to add or replace a secret, or remove_totp: true to clear it.
DELETE /api/v1/passwords/{id} editor Delete a password.
GET /api/v1/relationships any List relationships. Supports entity_type and entity_id.
GET /api/v1/relationships/{id} any Get a relationship.
POST /api/v1/relationships editor Create a relationship.
DELETE /api/v1/relationships/{id} editor Delete a relationship.
GET /api/v1/users admin List users.
GET /api/v1/users/{id} admin Get a user.
POST /api/v1/users admin Create a user.
PATCH /api/v1/users/{id} admin Update a user.
DELETE /api/v1/users/{id} admin Delete a user.
GET /api/v1/tokens any List your tokens; admins see all tokens.
POST /api/v1/tokens any Create a token. The raw value is returned once.
DELETE /api/v1/tokens/{id} any Revoke a token.
GET /healthz public Health check.

List endpoints return {"items": [...], "total": n}. Validation errors use {"error": "..."} with an appropriate 4xx status.

Document sections are ordered objects with an id, type, and title. Supported types are text, steps, images, checklist, callout, code, table, fields, and links. Depending on the type, send body, steps, images, items, tone, language, columns + rows, fields, or links. Images must use URLs returned by the upload endpoint.

Uploaded document images are stored unencrypted in <data-dir>/document-images/. They are only served to signed-in users, but they are not protected by the same AES-256-GCM encryption used for password secrets; back up the data directory accordingly.

Example: create and use a token

# Using a session cookie from the login example.
TOKEN=$(curl -s -b cookies.txt \
  -H 'Content-Type: application/json' \
  -H 'X-Requested-With: documentation-center' \
  -d '{"name":"automation"}' \
  http://localhost:8080/api/v1/tokens | jq -r .token)

curl -H "Authorization: Bearer $TOKEN" \
  http://localhost:8080/api/v1/configurations?limit=10

Security notes

  • Encryption at rest: password values, notes, and authenticator (TOTP) secrets are encrypted with AES-256-GCM. The key is derived from DOCUMENTATION_CENTER_SECRET or the generated secret.key. Back up the key with the database; without it, stored secrets cannot be recovered.
  • Password hashing: user passwords use bcrypt with cost 12.
  • Sessions: session tokens are random, stored hashed, sent in HttpOnly cookies with SameSite=Lax, and expire after DOCUMENTATION_CENTER_SESSION_TTL.
  • CSRF: cookie-authenticated mutations require the X-Requested-With: documentation-center header.
  • Authorization: roles are enforced by API middleware, not only by hiding UI controls.
  • Audit trail: sign-ins, content changes, password reveals, authenticator code reveals, user changes, and token operations are recorded.
  • Login throttling: failed sign-ins are rate-limited per IP/username pair, including failed authenticator codes and denied Duo pushes.
  • Two-factor authentication: TOTP secrets are generated per user, encrypted at rest, and verified with a ±1 time-window tolerance. Duo Push uses the server-side Duo Auth API credentials and stores only the user's Duo username. Enrollment and disabling both require the current password.
  • Headers: responses include CSP, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, and a same-origin referrer policy.
  • TLS: terminate TLS at a reverse proxy; cookies are marked Secure automatically when it forwards X-Forwarded-Proto: https. Do not expose the plain HTTP listener to the internet.
  • Backups: back up the entire data directory, including secret.key, while the app is stopped or use SQLite's online backup mechanism.

This project is intended for self-hosted use by a trusted team. It does not implement SSO, SCIM, per-record ACLs, or multi-tenant isolation. Review the code and harden the deployment for your environment.

Development

make fmt       # gofmt
make vet       # go vet
make test      # go test ./...
make check     # fmt + vet + test
make clean     # remove build artifacts
make install   # native install (system-wide automatically when run as root)
make install-system  # sudo ./install.sh --system
make uninstall # remove the install (pass ARGS="--purge", for example)
make uninstall-system  # sudo ./uninstall.sh --system

Project layout:

install.sh               native systemd installer (system-wide or per-user)
uninstall.sh             native systemd uninstaller
cmd/documentation-center/              application entry point
internal/api/            HTTP router, middleware, handlers
internal/config/         environment configuration
internal/model/          domain types and validation lists
internal/security/       bcrypt, AES-GCM, token helpers
internal/seed/           demo data
internal/store/          SQLite schema and data access
internal/web/            embedded HTML/CSS/JS and static helpers

The frontend is plain HTML, CSS, and JavaScript embedded into the binary with go:embed. It has no build step. All API calls use the same REST endpoints documented above.

License

No license has been specified yet. Add one before distributing the project.