A knowledge base is docs for your own team — engineers, customer support, ops, new hires. Runbooks, postmortems, onboarding material, internal process docs, architecture deep-dives. Different readership than the public docs + help center, different maintenance model, usually different privacy requirements.
Many teams split internal KB from public docs (Notion / Confluence / GitHub wiki for internal; NookDocs for public). But there's a case for unifying — single tooling, same editor, consistent voice, shared components. This guide covers the unified pattern and the split pattern; pick the one that matches your scale.
Public docs vs internal KB
| Axis | Public docs / help center | Internal knowledge base |
| Audience | Customers, prospects, integrators | Employees, contractors |
| Voice | Outward-facing, vetted | Terse, opinionated, operational |
| Maintenance | Scheduled (per release + quarterly) | Continuous, often ad hoc |
| Privacy | Public by default | Gated by default |
| Content types | Tutorials, reference, explanations | Runbooks, postmortems, decision docs |
| Search intent | "How do I X with Acme?" | "What's the current on-call runbook?" |
| Review cadence | PR review per change | Varies — sometimes none, sometimes heavier |
These differences push most teams toward separate tooling. But they can coexist — below.
Two architectures
Architecture 1: Separate tenant per audience
Run two NookDocs projects on separate subdomains:
docs.acme.com — public docs (open, indexed)
internal.acme.com — knowledge base (private, auth-gated)Pros:
Clean privacy story — internal tenant sets
seo.indexing: "noindex"+ middleware-level auth.Different themes / branding if your public docs are marketing-adjacent.
No leak risk — public readers literally can't reach internal content.
Cons:
Two repos, two build pipelines, two editor experiences.
Writers context-switch when a doc needs to live in both.
Best for: teams 20+ engineers where the KB has its own dedicated maintainers.
Architecture 2: Single tenant, internal pages gated
One NookDocs project. Public content surfaces normally; internal content lives under pages with hidden: true or authGroups: ["employees"] (when the auth layer ships — see personalization).
Pros:
Single source of truth — a page that's "mostly public but with an internal section" can use
<Visibility for="agents">-style splits (needs to be built — this is a roadmap extension of the existing<Visibility>component).One repo, one build, one editor.
Easier to move content from internal to public as features launch.
Cons:
Privacy-by-accident risk — a misconfigured page leaks internal content.
Public-facing search surfaces must exclude internal pages at multiple layers (sitemap + search + AI assistant RAG).
Auth layer needs to be real + battle-tested.
Best for: teams under 20 engineers, or teams where public docs and internal KB genuinely share content (integration partner docs, for example).
Today on NookDocs: Architecture 1 is the shipping pattern. Architecture 2 requires the auth layer + per-page viewer-based gating (both on the roadmap per personalization guide).
The four KB sections every team needs
1. Onboarding
Week-one + month-one reading for new engineers. Architecture overview, repo map, dev environment setup, deployment walkthrough, team norms.
2. Runbooks
Operational procedures. "How to deploy a hotfix", "Recovering from X incident", "Rotating the OAuth secret". Short, actionable, frequently used.
3. Decision records (ADRs)
Why we chose X over Y. Date, context, alternatives, decision, consequences. Lets future engineers understand legacy choices without polling Slack archeology.
4. Postmortems
After-incident writeups. Blameless. Actionable. Often the most-read KB pages because the lessons apply across teams.
Onboarding flow
A great onboarding KB sequence for a new engineer:
Day 0 — pre-start
Background reading sent before day 1: company-wide mission, product overview, primary customer persona. Non-technical if the hire hasn't started yet.
Day 1 — orientation
Team structure, who owns what, how to reach people. Access setup (GitHub, Slack, dashboards, VPN). Dev environment stand-up checklist.
Week 1 — first PR
Pointer to the repo, code style guide, small starter issue, pairing partner. Ship something to production by Friday.
Month 1 — core architecture
Deep-dive pages: request lifecycle, data model, deployment story, observability stack. 1 page per major system.
Month 1 — team norms
PR review expectations, on-call rotation, oncall handoff ritual, meeting culture, how we communicate (sync vs async).
Most onboarding KBs fail by treating month-1 content as day-1 reading. Staged disclosure matters.
Runbook pattern
Every runbook follows the same structure:
---
title: Rotate the OAuth client secret
description: Required monthly OR when exposure suspected. 15-minute process; requires deploy + coordinated user session invalidation.
---
## TL;DR
1. Generate new secret in IdP admin console.
2. Deploy code change referencing new env var.
3. Confirm new secret works + old still works (grace period).
4. Remove old secret from IdP after 24h.
## Prerequisites
- IdP admin access (Okta console)
- Deploy access to production
- On-call coordination for any downtime window
## Steps
<Steps>
<Step title="Generate new secret">
...
</Step>
...
</Steps>
## Verification
- [ ] Users can still log in (check analytics dashboard for active sessions)
- [ ] No error spike in observability (10 min after deploy)
- [ ] Old secret rejected after revoke (test with old creds)
## Escalation
If step 3 fails (new secret doesn't work), revert deploy immediately.
Old secret remains valid during grace period. Page on-call lead if
revert + retry needed.
## Change log
- 2026-04-15 — Added IdP console screenshot (ESM)
- 2025-11-02 — Updated to new Okta admin flow (KTA)Every runbook: TL;DR, prereqs, steps, verification, escalation, change log. Same every time. Readers under pressure scan for the right section.
ADR (Architecture Decision Record) pattern
---
title: "ADR-042: Use Postgres FTS instead of Elasticsearch for docs search"
description: 2026-04 decision. Trade-off between operational complexity and feature depth; FTS chosen for MVP.
---
## Context
We needed full-text search for tenant docs pages. Options considered:
- Elasticsearch (industry standard, high operational cost)
- Algolia (SaaS, fast, but adds a vendor)
- Postgres FTS (already in stack, less feature-rich)
## Decision
Ship Postgres FTS with pg_trgm + GIN index. Defer Elasticsearch
or Algolia until scale warrants.
## Consequences
**Good:**
- Zero new infrastructure to operate
- Syncs atomically with page writes (no eventual consistency)
- Free at current scale
**Trade-offs:**
- No fuzzy match beyond trigram (typos 1-2 chars work; longer distance don't)
- No synonym expansion without manual tables
- 100ms query latency at 100k pages; needs watching at 1M+
## Related ADRs
- ADR-038: Multi-tenant query isolation via project_id
- ADR-057: (Future) Embedding-based hybrid searchADRs are append-only. Never edit — supersede by writing a new ADR referencing the old one.
Postmortem pattern
---
title: "PM-2026-04-18: API 5xx spike during deploy #14592"
description: Root cause, user impact, fix, lessons. Blameless.
---
## Summary
2026-04-18, 14:02 UTC. Deploy #14592 introduced a null-pointer crash
in the rate-limiter. ~8% of requests returned 500 for 23 minutes
before rollback.
## Timeline (UTC)
- **14:02** — Deploy #14592 completes (canary 10% traffic)
- **14:04** — Sentry error rate breaches alert threshold
- **14:06** — On-call paged
- **14:09** — Root cause identified (nil check missing in new code path)
- **14:12** — Rollback started
- **14:25** — Rollback complete; error rate back to baseline
- **14:30** — Customer comms sent via status page
## User impact
- ~2,400 failed API requests across ~180 tenants
- Support tickets from 8 tenants
- No data loss (idempotency keys preserved; failures returned HTTP
500 without partial writes)
## Root cause
The rate-limiter code path added in #14592 accessed `request.user.tier`
without checking whether `user` was set. Unauthenticated requests
(API key without user) produced nil access → crash.
## Fix
- Short-term: rolled back #14592 (deployed 14:12).
- Short-term: hotfix in #14599 with nil guard shipped 15:30.
- Long-term: add unit test coverage for unauthenticated code paths.
## Lessons (blameless)
- The deploy pipeline's canary at 10% DID detect the issue — this is
working as designed. Time-to-detect was 2 minutes.
- Time-to-revert was 10 minutes; could be faster with better
tooling. Opened infra ticket #8812 to investigate.
- Test coverage gap: unauthenticated rate-limiter paths. Added to
test suite in #14600.
## Action items
- [ ] @alice — add pre-commit check for nil access in rate-limiter
- [ ] @bob — faster rollback tooling (target TTR <5min)
- [ ] @carol — document canary traffic split in the deploy runbookBlameless > detailed blame. Action items with owners + accountability, not just "we should do better."
KB privacy + auth
For Architecture 1 (separate internal tenant):
seo.indexing: "noindex"site-wide in the internal tenant'snookdocs.config.json. Ensures search engines don't index internal content.Middleware auth at the proxy / CDN layer. Cloudflare Zero Trust, Vercel auth, or your own OAuth-proxy in front of the internal domain.
No anonymous read — every request requires a valid session cookie or bearer token.
For Architecture 2 (unified tenant with internal pages):
Roadmap: per-page
authGroupsgating (not yet enforced at runtime).Today: use
hidden: truefrontmatter for "obscure by URL" content. NOT true privacy — if a URL leaks, content is readable. Don't put secrets here.
When your KB grows past 100 pages
Scale challenges:
IA rot — what started as 5 sections is now 20. Revisit navigation IA.
Stale content explosion — 100 pages of runbooks, many from years ago. Apply maintenance quarterly audits more aggressively here than public docs.
Search quality — more pages = more noise. Consider per-team scope filters in search.
Ownership fragmentation — without clear per-page owners, no one updates. Add a
owner:frontmatter field + quarterly review assignment.
At 500+ pages, consider splitting the KB further — per-team micro-KBs rolled up to a top-level index.
Common KB mistakes
Related
Developer documentation — external dev docs sibling
Help center — end-user support docs sibling
Maintenance — KB decay is real
Navigation IA — scaling IA for 100+ pages
Personalization — per-group gating (roadmap)
SEO noindex — keeping KB out of search engines