- Go 46.1%
- JavaScript 31.5%
- CSS 10.9%
- HTML 5%
- Shell 4.3%
- Other 2.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| bin | ||
| cmd | ||
| data | ||
| internal | ||
| scripts | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| install.sh | ||
| Makefile | ||
| README.md | ||
| to.txt | ||
| uninstall.sh | ||
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+Kopens 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, andviewerroles 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-centersystem account (use--service-user rootto run as root instead); - installs the binary to
/usr/local/bin/documentation-center; - writes
/etc/documentation-center/documentation-center.env(mode0600, owned by root); - stores the database and encryption key in
/var/lib/documentation-center/, owned by the service account; - enables
documentation-center.serviceinmulti-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
totpcode from the login request is verified. A missing or invalid code returns401withrequires_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.
- In the Duo Admin Panel, create an Auth API application and copy its integration key, secret key, and API hostname.
- Set
DOCUMENTATION_CENTER_DUO_IKEY,DOCUMENTATION_CENTER_DUO_SKEY, andDOCUMENTATION_CENTER_DUO_API_HOST. All three are required; startup fails if only some are set. - Restart the server. Security → Two-factor authentication now offers both methods.
- 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_SECRETor the generatedsecret.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
HttpOnlycookies withSameSite=Lax, and expire afterDOCUMENTATION_CENTER_SESSION_TTL. - CSRF: cookie-authenticated mutations require the
X-Requested-With: documentation-centerheader. - 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
Secureautomatically when it forwardsX-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.