MYDOC24 · KNOWLEDGE PLATFORM

myDoc24

Knowledge Base

Est. 2026 · Solar MD
01

About myDoc24

A documentation platform you actually own

myDoc24 is a knowledge platform for teams who want their writing to live on their own infrastructure — your domain, your servers, your auth, your rules. Write articles in Markdown, organise them however your team thinks, and publish to a clean editorial reader your visitors will actually want to read.

Built around the way teams write

Author in Markdown. A live editor with attachments, code blocks, tables, and embedded media. Save and the content is sanitised, rendered to HTML, and ready to publish.

Organise content your way. Articles and editable sections you can reorder, rename, and restructure without rewriting a single page.

Control who reads and who edits. Roles for readers, writers, and admins. Magic-link sign-in, Google and Microsoft single sign-on, or a classic password flow with forgot-password and admin-issued resets. Sign-up policy is yours to set — invite-only, open to anyone, or somewhere in between.

Automate the boring parts. A REST API with bearer tokens lets you upload articles from scripts, sync from another system, or hand authoring off to an LLM agent. The OpenAPI 3.1 spec is published openly at /api/openapi.yaml — point any tool that speaks OpenAPI at it.

Make it yours. Title, tagline, logo, and accent colour are all editable from the admin. No code change to rebrand.

Five minutes from zero to live

Install with npm against your existing MySQL, or docker compose for a bundled stack. Boot the server, open the URL, and a five-screen wizard walks you through database connection, site settings, and the first admin account. No SSH required for the happy path.

Where to go next

  • Changelog — what's shipped recently and what's coming.
  • Support — how to reach us with questions.
  • Clients — explore live deployments running on the platform.
02

Download & install

Get the platform

myDoc24 ships as a downloadable source bundle. Run it on your own infrastructure — your domain, your auth, your database, your container (or no container at all). Three install paths cover the common cases; pick whichever matches your stack.

Download

kb-app-latest.tar.gz (~410 KB, source-only — no node_modules baggage)

The tarball contains the full Next.js source plus a Dockerfile, a reference docker-compose.yml, deploy/ templates for native installs, and docs/DEPLOYMENT.md with the complete operational contract.

What you'll need

  • A Linux box you control — Proxmox LXC, plain VPS, EC2, your laptop
  • Node.js 20+ (any current LTS)
  • MySQL 8 or MariaDB 10.5+ (or a managed instance — see the database compatibility section below)
  • A domain name pointing at the box (for TLS and OIDC redirect URIs)

Option 1 — Native install (LXC / VPS / bare-metal)

No Docker required. Solar MD's own three deployments run this way: systemd + nginx + native Node. The tarball ships everything you need.

curl -L https://mydoc24.org/downloads/kb-app-latest.tar.gz | tar xz
cd kb-app
npm ci && npm run build
sudo ./deploy/install-native.sh --domain=kb.example.com
sudo certbot --nginx -d kb.example.com

The install-native.sh script:

  1. Creates a kb-app system user.
  2. Substitutes <DOMAIN> and <INSTALL_DIR> into the supplied systemd unit (deploy/systemd/kb-app.service) and nginx site (deploy/nginx/kb-app.conf), then drops them into /etc/.
  3. Scaffolds a minimal .env.local with a freshly-generated AUTH_SECRET.
  4. systemctl enable --now kb-app + systemctl reload nginx.
  5. Waits for the app to respond on port 3000.

After certbot finishes, visit https://kb.example.com — the first-run wizard appears. Paste your DATABASE_URL, click through five screens, and you're live.

For a manual walkthrough (without the helper script), see deploy/README.md in the tarball.

Option 2 — Docker Compose (turnkey, bundles MySQL)

For customers who don't already have MySQL available. The reference compose file in the tarball wires kb-app up against a sibling mysql:8 container.

curl -L https://mydoc24.org/downloads/kb-app-latest.tar.gz | tar xz
cd kb-app
cp .env.compose.example .env

Edit .env and set three secrets (any random strings — generate via openssl rand -base64 32):

AUTH_SECRET=<paste>
MYSQL_ROOT_PASSWORD=<paste>
MYSQL_USER_PASSWORD=<paste>

Then:

docker compose up -d

Visit http://your-server:3000 for the wizard.

Option 3 — Build your own container (k8s / Coolify / ECS / Portainer / …)

If you've already standardised on your own container orchestration, kb-app slots in cleanly. The repo includes a Dockerfile you can build from source, and docs/DEPLOYMENT.md documents the full operational contract:

  • Required env: just AUTH_SECRET (one base64 string). Everything else is collected by the wizard or editable from /admin/settings.
  • Port: 3000 inbound only.
  • Volume: mount /data (or wherever KB_DATA_DIR points) for persistent config.json + .setup-lock. Asset blobs live INLINE in MySQL — no second backup target.
  • Healthcheck: GET /api/health returns 200 + {"ok":true,"db":"up"} when reachable, 503 + {"ok":false,"db":"down"} when not. Wire into k8s livenessProbe + readinessProbe, Docker healthcheck:, or your monitoring of choice.
  • Graceful shutdown: 30 s terminationGracePeriodSeconds is plenty — kb-app drains the mysql2 pool in ~3 s typical.

Build your image:

docker build -t your-registry/kb-app:latest .
docker push your-registry/kb-app:latest

Then point your platform at it. docs/DEPLOYMENT.md includes worked sketches for k8s, Coolify, Render, and plain docker run.

First-run wizard (all three paths)

Five screens:

  1. Welcome — version + doc links.
  2. Database — paste your DATABASE_URL. The wizard tests the connection before letting you continue; a bad URL never reaches disk. Same screen whether you're on npm, Docker Compose, or BYO container.
  3. Site settings — title, tagline, accent colour, icon. All editable later from /admin/settings.
  4. First admin account — email, name, password. This user becomes the bootstrap admin with every role. Subsequent users join via the invite system or the open-signup toggle.
  5. Done — wizard locks itself permanently (/data/.setup-lock) and lands you on /auth/signin.

Database compatibility

kb-app uses the MySQL wire protocol via the mysql2 driver. Anything that speaks it should work:

Flavour Status
MySQL 8.0+ ✅ Tested. Solar MD's reference DB.
MariaDB 10.5+ ✅ Tested. Drop-in replacement.
Amazon RDS for MySQL ✅ Tested. Standard connection string.
Amazon Aurora MySQL (3.x, MySQL 8 compat) ✅ Tested.
PlanetScale ✅ Tested. Use the dashboard's connection string (TLS pre-configured).
DigitalOcean Managed MySQL ✅ Tested. CA cert mount documented in DEPLOYMENT.md.
MySQL 5.7 ⚠️ Best-effort. Some JSON SQL paths may differ. Not regression-tested.
Postgres / SQLite / MS SQL ❌ Unsupported. Schema uses MySQL-specific features (LONGBLOB, FULLTEXT, JSON_EXTRACT). A different engine is a 2–4 week rewrite.

After install

A few common settings you'll likely want to flip in /admin/settings:

  • Sign-in providers — Magic-link email, Google OAuth, Microsoft Entra ID. Paste credentials per provider, toggle on, done. Per-provider consent screen verification (Google, Microsoft) is your responsibility per deployment.
  • Public vs privateKB_PUBLIC_READ_MODE. Off = every read requires auth. On = anonymous browsers can read articles, edits still require sign-in.
  • Embedded widget — paste the <kb-widget data-base="https://your-domain"> snippet into any other web app to surface your knowledge base inside it. Contract in the API Docs tab.

Backups

Just mysqldump. Asset blobs + config + everything live in MySQL. No second backup target needed.

mysqldump -h <host> -u <user> -p<password> <db> > kb-app-backup-$(date +%F).sql

Upgrades

Download the newer tarball, extract over your install, run migrations:

curl -L https://mydoc24.org/downloads/kb-app-latest.tar.gz | tar xz
# rsync over your install (skip node_modules / .next / .env.local / data):
rsync -a kb-app/ /opt/kb-app/ --exclude=node_modules --exclude=.next --exclude=.env.local --exclude=data
cd /opt/kb-app
npm ci
npm run db:migrate
npm run build
systemctl restart kb-app   # or: docker compose up -d   # or: kubectl rollout restart deployment/kb-app

Migrations are idempotent — a failed mid-deploy is safe to re-run.

Help

Full operational reference: docs/DEPLOYMENT.md inside the tarball.

For questions, see the Support tab on this site. For the REST + agent-authoring contract, see the API Docs tab. For end-user docs (how to use a published deployment), see the User Guide tab.

03

myDoc24 Knowledge Base — user guide

myDoc24 Knowledge Base — user guide

A self-hostable knowledge-base engine. Same build artifact, two deployment shapes:

  • Private KB — auth required for every read. Internal handbooks, per-tenant docs.
  • Public KB — anonymous reads allowed, only authors can edit. Product docs, open wikis.

The shape is picked by one env var (KB_PUBLIC_READ_MODE). Everything else — auth providers, branding, SMTP, the guest API key — is editable live from /admin/settings after install.

For the REST surface, see the API docs.


Install

Two paths. Pick Docker Compose to spin up the app + MySQL in one command, or npm direct if you already have a MySQL.

Docker Compose (turnkey)

curl -fsSL https://get.kbapp.dev/install.sh | bash

The installer drops docker-compose.yml + .env (with random secrets) into ./kb-app, pulls the image, starts both containers, and prints the URL to open. The .env it generates only has what you need — MYSQL_ROOT_PASSWORD, MYSQL_USER_PASSWORD, AUTH_SECRET, KB_HTTP_PORT (default 3000).

Day-2 ops:

Action Command
Upgrade docker compose pull && docker compose up -d
Tail logs docker compose logs -f kb-app
Stop docker compose down
Wipe (DESTROYS DATA) docker compose down -v && sudo rm -rf data mysql-data

State lives in ./data (config, blob storage, auth secret) and ./mysql-data (MySQL files). Back both up alongside .env.

MySQL's port 3306 is not published to the host — the DB is only reachable from the kb-app container over the private compose network. Edit docker-compose.yml to expose it if you want CLI access.

npm direct (BYO MySQL)

git clone https://github.com/solarmd-pty-ltd/knowledgeBaseApp.git
cd knowledgeBaseApp
npm ci
npm run kb:build
AUTH_SECRET="$(openssl rand -base64 32)" KB_DATA_DIR=./data npm run kb:start
# → http://localhost:3000 → finish setup in the browser

The wizard asks for a MySQL connection URL on step 1. Paste yours (mysql://user:password@host:3306/dbname) and it'll Test → Save → run migrations → create the first admin → lock itself.

The only required runtime env vars are AUTH_SECRET and KB_DATA_DIR. Everything else is editable from the browser later.


Setup wizard

Three steps. After step 3 the wizard is unreachable (/setup/).

  1. Welcome — confirms the install URL.
  2. Site name + first admin — collects the display name and bootstrap admin email / name / password. Magic-link sign-up needs SMTP, which isn't configured yet, so the first admin gets a password.
  3. Finalize — runs DB migrations, creates the admin with full kb:* permissions, locks the wizard, signs you in.

To re-open the wizard you'd have to delete data/config.json, data/.setup-lock, and truncate kb_user. Deliberately manual.


Configure providers

Once signed in, go to /admin/settings. Everything is DB-backed with a 30-second cache — saves apply within 30 s, no restart needed.

Panel Holds
Branding Title / description / icon
Keycloak AUTH_KEYCLOAK_ID, _SECRET, _ISSUER, NEXTAUTH_URL
Application KB_PUBLIC_READ_MODE, KB_GUEST_API_KEY, KB_PUBLIC_BASE_URL
Environment DATABASE_URL editor — writes to .env.local, restart required
Email (SMTP) host / port / from address — magic-link + forgot-password need this
Password Toggle email + password sign-in on/off
Sign-up policy OPEN (anyone can sign up) vs INVITE (admin issues invites)
Cross-app Provisioning secret for paired host apps (rotate on demand)

Each provider has an on/off toggle alongside its credentials — useful for temporarily disabling a misconfigured provider without clearing the values.

Google + Microsoft OAuth are env-var only on kb-app (AUTH_GOOGLE_* / AUTH_MICROSOFT_*). Each customer deployment needs its own per-domain consent-screen review at Google Cloud Console / Microsoft Entra ID, so there's no admin UI to configure them — set the env vars and they appear on /auth/signin automatically.

The Environment panel is the one exception

DB connection saves write to .env.local on disk (atomic, with a timestamped backup), gated by a mandatory "Test connection" so a bad URL never reaches disk. After saving, restart the service: systemctl restart kb-app on a VM, docker compose restart kb-app on the compose bundle. If the new value breaks startup, SSH in and restore from the backup path shown in the success banner.


Signing in

Open /auth/signin. The buttons shown depend on which providers are configured and enabled (the on/off toggle on /admin/settings):

  • Magic-link email — enter your address, click the link in your inbox. Needs SMTP.
  • Continue with Google / Microsoft — appears when AUTH_GOOGLE_* / AUTH_MICROSOFT_* env vars are set.
  • Continue with Keycloak — appears when KC config + toggle are on.
  • Email + password — for accounts created by an admin or anyone who's set a password via "Forgot password?".
  • System admin login — break-glass backdoor for operators. Only visible if explicitly set up via a .sysadmin-secret file on the server.

After sign-in you land on the hub at /.


Reading

The home page is a hub:

  1. Hero — title + description + search bar.
  2. Folders grid — every top-level folder as a card with a recursive count of published articles.
  3. Uncategorised — articles that don't sit under a folder.

Click a folder to drill in (/f/<slug>). Click an article to read it (/a/<slug>). The article page has a sticky TOC sidebar on the right at lg+ widths — click any heading to jump.

Search is full-text via MySQL MATCH ... AGAINST, ranked, max 50 hits across both title and body.


Authoring

You need the kb:write:pages permission to author. Operators assign it via the user-menu → "Permissions" or /admin/users.

  1. AdminNew article → fill in a title.
  2. You're redirected to /admin/edit/<slug> — a ProseMirror editor with headings, lists, bold/italic, links, images (drag-drop or paste, 50 MB max), tables, video, YouTube/Vimeo embeds, four callout variants (note / tip / warning / danger), and a closed palette of text/highlight colours.
  3. Save Draft stores your work-in-progress; only other authors see it.
  4. Publish flips status to PUBLISHED, sanitises the body, revalidates the public reader, drops the draft.
  5. Archive hides without deleting; reversible from /admin.

Editorial style — title conventions, the matched-pair rule (process + data dictionary), screenshot workflow, callout variant choice — lives in ARTICLE-AUTHORING.md. Read that before authoring real content.

Editor gotchas:

  • Mod-Enter breaks out of a callout / blockquote / code block.
  • Mod-K opens the link dialog (external URL or KB-article picker).
  • Mod-B / Mod-I / Mod-U for bold / italic / underline.
  • Tables get a floating row/col menu when the caret is inside a cell.

Personal access tokens

Any signed-in user with at least one kb:* role can mint tokens at /me/tokens (user-menu → "My API tokens").

  • Scope = your current permissions, snapshotted at issuance. A read-only user gets a read-only token. You can't escalate by minting.
  • Show-once. The raw kbpat_… string is printed exactly once; the DB stores only the SHA-256 hash.
  • Revoke from the same page. 401s immediately on the next request, no grace window.

Send as Authorization: Bearer kbpat_… on any /api/kb/* request. See the API docs for the full reference.


Sysadmin backdoor

A non-Keycloak credential login, disabled by default. Useful for bootstrapping a fresh deployment, editing KC config when KC is down, or emergency access.

Enable via a .sysadmin-secret file (not .env.local — Next.js's dotenv-expand mangles the $2b$12$... hash). Sign in as KB_SYSADMIN_USERNAME from the "System admin login" section of /auth/signin; sessions carry the full kb-admins role set. Every attempt + every config write goes to kb_audit. Rate-limited to 5 failed attempts per 15 min per IP.

Setup details (hash script, file paths, the dotenv gotcha) live in the operator README.


Where to go next

  • Authoring rulesARTICLE-AUTHORING.md
  • REST APIAPI docs
  • Operator READMEREADME.md (nginx + systemd + runbooks)
04

myDoc24 Knowledge Base — API docs

myDoc24 Knowledge Base — API docs

Programmatic surface for reading articles, publishing content from scripts or AI agents, and embedding KB content into other applications.

All endpoints live under /api/kb/. CORS is Access-Control-Allow-Origin: * on read endpoints + widget.css + events so embedded widgets can fetch cross-origin.

For the human-facing side of the product, see the user guide.


Authentication

Every /api/kb/* request needs auth. There are four flows; pick the one that matches the caller:

Flow Header Use when
Personal access token Authorization: Bearer kbpat_<32 base62> Ad-hoc agent / script run by a signed-in user. Mint at /me/tokens.
Keycloak access token Authorization: Bearer <JWT> Authenticated user driving the call from their browser session.
Service-account client Authorization: Bearer <JWT> Unattended automation. KC client-credentials grant.
Guest API key X-Kb-Guest-Key: <secret> Server-to-server reads from a paired host app. Read-only.
Anonymous (no auth) Public-read deployments only — KB_PUBLIC_READ_MODE=true. Read-only.

Personal access tokens

Minted at /me/tokens. Scope is your current permissions, snapshotted at issuance — a read-only user gets a read-only token. The raw kbpat_… is shown once; the DB stores only the SHA-256 hash. Revoke from the same page; revoked tokens 401 immediately.

Service-account flow (unattended automation)

The kb-import-agent client exists in the realm config. Get its secret from the KC admin console → assign realm role kb:write:pages to its service account → mint tokens with:

TOKEN=$(curl -sS -X POST \
  -d "grant_type=client_credentials" \
  -d "client_id=kb-import-agent" \
  -d "client_secret=$KB_IMPORT_AGENT_SECRET" \
  https://<kc-host>/realms/<realm>/protocol/openid-connect/token \
  | jq -r .access_token)

Tokens last 5 minutes. Re-mint on demand — there's no refresh-token flow for client-credentials grants.

Guest API key

Shared secret in KB_GUEST_API_KEY, validated with crypto.timingSafeEqual. Materialises a synthetic principal { sub: "kb-guest", roles: ["kb:read:pages","kb:read:assets"], groups: [] }read-only, rejected for any write/manage permission.

Distribute the key out-of-band to the paired host application. Rotate from /admin/settings → Application → Guest API key. DB-backed, 30-second cache, no restart.


Read endpoints

All require kb:read:pages (or kb:read:assets for blobs), unless KB_PUBLIC_READ_MODE=true is set.

Endpoint Returns
GET /api/kb/folders Published tree of { id, name, slug, folders, articles }
GET /api/kb/article?slug=… Single article body — only when status is PUBLISHED
GET /api/kb/search?q=… FTS hits, ranked, max 50
GET /api/kb/blobs/[id] Binary stream with Cache-Control: public, max-age=31536000, immutable
GET /api/kb/events SSE channel — pushes tree-changed on every publish / archive / folder mutation. Pass token as ?token=… (EventSource can't set headers)
GET /api/kb/widget.css Stylesheet for embedded widgets. Always public, no auth

Example:

curl -sS -H "Authorization: Bearer kbpat_…" \
  "https://kb.example.com/api/kb/search?q=invoicing" | jq '.'

Write endpoint — import a Markdown article

POST /api/kb/admin/articles/import

Designed for AI agents authoring Markdown and having the server convert it to ProseMirror JSON + sanitised HTML.

Request:

curl -sS -X POST https://kb.example.com/api/kb/admin/articles/import \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title":      "How invoicing works",
    "slug":       "how-invoicing-works",
    "folderSlug": "getting-started",
    "keywords":   "invoice, billing, customer",
    "markdown":   "# Heading\n\nBody text…",
    "publish":    true
  }'

Response:

{ "id": 17, "slug": "how-invoicing-works", "status": "PUBLISHED" }

Behaviour:

  • Upsert by slug. If slug matches an existing article, that row is updated in place. Otherwise a new article is created and the slug is derived from the title (with -2, -3 collision suffixes).
  • publish: true runs the publish flow: status → PUBLISHED, paths / + /a/[slug] revalidated, draft row dropped, SSE tree-changed broadcast.
  • publish: false (default) writes to kb_article_draft — status stays DRAFT (or whatever it was) and the body is visible in the admin editor for human follow-up.
  • Markdown coverage: headings, paragraphs, lists, blockquotes, fenced + inline code, bold / italic / links, hr, hard-breaks, plus GFM-style callout blockquotes (> [!NOTE|TIP|WARNING|DANGER]<div class="kb-callout kb-callout-{variant}">). Tables, video, embed, and image-with-align remain editor-only.
  • Folder must exist and not be soft-deleted, otherwise 400.
  • created_by / published_by / last_saved_by get the caller's sub claim — real KC user ID under PAT/KC flows, service-account UUID under client-credentials.

Permission matrix

Endpoint Required permission
GET /api/kb/folders kb:read:pages
GET /api/kb/article kb:read:pages
GET /api/kb/search kb:read:pages
GET /api/kb/blobs/[id] kb:read:assets
GET /api/kb/events kb:read:pages (via ?token=…)
GET /api/kb/widget.css public
POST /api/kb/admin/articles kb:write:pages
POST /api/kb/admin/articles/import kb:write:pages
PUT /api/kb/admin/articles/[id]/draft kb:write:pages
POST /api/kb/admin/articles/[id]/publish kb:manage:pages
POST /api/kb/admin/articles/[id]/archive kb:manage:pages
DELETE /api/kb/admin/articles/[id] kb:manage:pages
POST /api/kb/admin/folders kb:manage:folders
PUT /api/kb/admin/folders/[id] kb:manage:folders
DELETE /api/kb/admin/folders/[id] kb:manage:folders
POST /api/kb/admin/blobs kb:write:assets
GET/POST /api/kb/admin/tokens kb:manage:groups
DELETE /api/kb/admin/tokens/[id] kb:manage:groups
GET /api/kb/admin/tokens/[id]/usage kb:manage:groups

Permissions are Wiki.js-style. Roles map to KC groups (kb-readers, kb-editors, kb-managers, kb-admins). Path-scoped overrides live in kb_page_rule and apply only to session/Bearer calls — PATs and the guest principal always carry groups: [], which short-circuits the rule branch (rules cannot grant or revoke PAT / guest access).


Errors

Status When
400 Missing required field, unknown folderSlug, parse fail
401 Missing / invalid / expired Bearer token
403 Token valid but lacks the required permission
404 Article id / slug not found

A 401 mid-session under PAT or KC flow means the token expired — re-fetch it from the source. Under client-credentials, re-run the grant.


Where to go next

  • Human-facing usageuser guide
  • Full agent-API referenceAGENT-API.md in the repo
  • OpenAPI specGET /api/openapi.yaml on each deployment
05

Changelog

This section hasn’t been written yet. An administrator can author it from the admin dashboard — once published it will appear here in place of this placeholder.

06

Support

This section hasn’t been written yet. An administrator can author it from the admin dashboard — once published it will appear here in place of this placeholder.

07Network

Our knowledge bases

The same engine powers customer knowledge bases across the Solar MD network. Follow a link to read another deployment.