1Context
A credential broker and knowledge wiki built for collaborative work between humans and AI agents · Internal project page · Updated 2026-04-17
1Context is a self-hosted knowledge base and credential broker designed from the start for collaboration between humans and AI agents working on the same projects. It uses a stock BookStack installation as the underlying wiki engine and adds two parallel surfaces on top: a WikiWand-inspired editorial layer for human readers, and a token-efficient, discoverable surface for any AI agent that's handed a 1Context URL.
The thesis is that the most useful place to store evolving project context — design decisions, in-flight work, troubleshooting notes, infrastructure runbooks, capability tokens — is somewhere both humans and agents can read, write, and link to. URLs are the simplest unit of context handoff. 1Context invests heavily in making both audiences productive against the same URLs.
Overview
A 1Context deployment is a stock BookStack v26.03+ instance with the
bookstack-opcontext-module theme installed. The theme is a static asset
bundle (HTML + CSS + JS, no PHP) wired in via BookStack's standard theme module
mechanism. It overlays its own UI on top of BookStack's existing chrome and uses
BookStack's REST API for search and content retrieval. Capability tokens, oauth
flows, and the AI chat backend live outside BookStack in a small companion service
referred to as the broker; pages document but do not contain those secrets.
The project's design rule of thumb: every user-facing setting maps to one
data-* attribute on the <html> element. CSS reacts
via attribute selectors and shifts design tokens; JavaScript only writes
<html>.dataset and localStorage. The CSS does the
rest. This keeps the rendered DOM small and the state model legible.
Origin and intent
The immediate motivation came from observing how often coding agents need project-specific context to do useful work, and how brittle the conventional handoffs are: pasting long blocks of context into a chat, sharing a Notion link that an agent can't read, or relying on the human to re-explain decisions that were already documented somewhere. A wiki is the natural place to keep that context — what makes 1Context distinct is that the wiki is treated as a first-class agent surface alongside the human-facing one.
The design also commits to using a generic underlying wiki engine (BookStack) rather than building a bespoke storage layer. That keeps the data portable, the authoring experience familiar, and the self-host story straightforward.
Architecture
The system has three layers, only the first of which is in this repository. The other two are referenced here for completeness and tracked in their own repos.
Theme
The theme — wiki-engine/theme/css/theme.css + chrome JS at
wiki-engine/theme/js/enhance.js — is bundled by Vite into the
static site that preview/'s HTML templates pull in via
preview/main.js. In dev it runs under Vite with hot reload; in
production it's a pre-built bundle in dist/. The theme is
responsible for the WikiWand-style editorial
layer, the right rail, the customizer, the search and bookmarks modals, the AI
chat panel, the agent-view toggle, and all the mobile-specific affordances.
Agent surface
A parallel set of affordances for any AI agent that fetches a page URL, designed
so the agent can find the token-efficient form of the content without being
taught about 1Context specifically. This includes a canonical
.md URL alongside every .html URL, an
HTTP Link header (server-side, planned), a
<link rel="alternate" type="text/markdown"> in
<head>, OpenGraph and JSON-LD tags, an in-page informational
note placed after the lead paragraph, and a Reader / Agent view toggle that lets
a human inspect what the agent would receive. The full design rationale is in
agent-ui.md.
MCP server
A planned (not yet implemented) Model
Context Protocol server exposing typed tools — list_pages,
read_page(slug, format=md), search,
recent_changes — that any MCP-aware agent (Claude Code, Claude
Desktop, Cursor, others) can call directly. This is the lossless agent-to-agent
context handoff path; the URL stack improves the WebFetch experience marginally
while MCP solves the handoff problem completely. Tracked as Phase 8 in
THEME_PHASES.md.
Wiki engine
Main article: Wiki engine
The wiki itself is generated by a small static wiki engine that takes markdown
sources (with frontmatter) and emits every surface a wiki needs in one build:
themed HTML, clean .md twins, talk pages, and the agent-discovery
files (llms.txt, llms-full.txt, docs-index.json).
The engine is in the same family as Quartz
and Foam — static-first, no
database — but with first-class agent surfaces those tools don't ship by default.
The engine is being built as we use it. 1Context itself is the first
wiki it powers; every page on this site is content the engine renders. Code lives
in wiki-engine/ in the repo (currently mid-migration from the parent
dirs). Storage is adapter-based — today filesystem, with Puter DB and BookStack
adapters planned so the same engine can later read content from a database or an
existing wiki API. The agent-readable
talk page tracks open work; new contributors
(human or agent) start there.
User experience
The human-facing layer is patterned closely on
WikiWand's reader-mode aesthetic, with
the navigational primitives reverse-engineered from observation of the live
product (probe scripts in tools/probe-wikiwand-*.py) and tuned with
three rounds of empirical sweep-testing. The features are organized into seven
phases.
Right rail and customizer
A 5-icon right rail (Account, Search, Bookmarks, Customize, AI Chat) lives fixed
to the right edge of the page. The customizer opens as a side drawer with seven
segmented controls — Theme, Table of Contents, Article Width, Font Size, Links
Style, Cover Image, Border Radius, Article Style — each writing to one
data-* attribute on the root. Defaults were chosen after a
probe-debt review of WikiWand's actual deployment: links style defaults to
color, border radius defaults to square. (Phase 1)
Table of contents chrome
A sticky TOC sidebar with a hamburger toggle and a current-page label at the top.
Section headings get click-to-collapse chevrons that toggle their h3 sublist with
per-page localStorage persistence. Long appendix sections (References,
Further reading, External links) are auto-wrapped in <details>
elements so they don't dominate the article. Scroll-spy via
IntersectionObserver highlights the current section. (Phase 2)
Hover preview cards
Hovering a cross-page link surfaces a 320px preview card with the target page's
title, thumbnail (if any), and a multi-line snippet. Implemented against the
live Wikipedia REST API in the preview environment; the production wiring will
point to a custom /api/peek/{book}/{page} theme route since
BookStack has no native page-summary endpoint. Hover delays of 300ms in / 100ms
out, scroll-dismiss with a 40px anchor threshold, in-flight token to avoid
stale renders, and an LRU cache. Suppressed entirely on touch devices via
matchMedia('(hover: hover)'). (Phase 3)
Search and bookmarks modals
⌘K opens a center-modal search dialog (against the Wikipedia title-search API in
preview, against BookStack's GET /api/search in production). Results
show a side preview pane on desktop; arrow keys navigate; Enter selects. ⌘B
opens a center-modal bookmarks dialog backed by localStorage; the
current page can be added or removed via a pill that toggles label based on
state. Both modals share a focus-trap, Esc-to-dismiss, and a backdrop-blurred
scrim. (Phase 4)
AI chat panel
⌘I (or the rail's chat icon) opens an AI chat panel with two display modes
— a floating bubble drawer for ad-hoc questions, and a fixed side panel for
extended sessions — controlled by a two-attribute state machine
(data-ai-display × data-ai-panel-visibility). The fixed panel is
drag-resizable between 360px and 720px with the width persisted; in fixed mode
the body reserves space via padding-right: var(--ai-panel-width).
The fixed-panel toggle is hidden below 1280px since the article would otherwise
be squished into a sliver. The preview's demo bot streams the article's first
real paragraph as a stand-in for a real LLM call. (Phase 5)
Reader / Agent toggle
A segmented control in the header — eye icon for Reader, code icon for Agent —
lets a human switch the article between the rendered HTML view and an
IDE-styled monospace dark frame showing the raw markdown that was fetched from
the <link rel="alternate"> URL. This is both a marketing
surface ("see exactly what your AI reads") and a debugging tool. State is
stored in sessionStorage only, never carrying across browser
sessions, to avoid bleeding a human's choice into a co-resident agent's session.
Reader is always the default on each page load. (Phase 7)
Mobile experience
Mobile took three rounds of dedicated hardening because the user-found regressions made it clear that resizing the viewport in headless Chromium was missing a large class of bugs. The current mobile layer is verified across iPhone 13 / WebKit, Pixel 5 / Chromium, and iPad Pro 11 / WebKit using Playwright device emulation (Tier 2 + 3 mobile testing).
Off-canvas drawer
At narrow widths (max-width: 960px) the TOC switches from the
desktop's in-flow column to a left-edge slide-out drawer with a backdrop scrim.
The drawer is pinned top: calc(var(--header-height) + 5px) so the
parent 1Context header chrome stays visible behind it. data-toc is
force-defaulted to "hidden" at narrow widths on first load, and
re-synced when the viewport crosses the breakpoint, so a user's saved desktop
preference doesn't auto-open the drawer when they switch to a phone.
iOS scrim-dismiss
iOS Safari does not always synthesize click events from taps on
non-interactive <div>s, even when cursor: pointer
is set. Document-level delegated click handlers will silently fail to fire,
making any drawer/modal scrim untappable on iOS. The fix — captured in the
addScrimDismiss helper in enhance.js — attaches both
a click handler and a touchend handler (with
preventDefault to suppress the would-be synthesized click) directly
to the scrim element. cursor: pointer is added too as a
belt-and-suspenders heuristic. Applied to the TOC drawer scrim, the modal
scrims (search and bookmarks share the same class), and the customizer overlay.
Safe-area insets
The viewport meta now declares viewport-fit=cover so iOS exposes
env(safe-area-inset-*) to CSS. The right-rail FAB stack and the AI
bubble FAB use max(var(--size-N), env(safe-area-inset-N)) for their
bottom and right offsets, keeping them clear of the iPhone home indicator and any
rounded-corner notches.
Body scroll lock
When any overlay is open (TOC drawer, customizer, search modal, bookmarks
modal, or — at narrow widths only — the full-screen AI panel) the body is
locked into position: fixed at the saved scroll offset, with a
refcount so multiple overlays can stack cleanly. On unlock, the saved scroll
position is restored. This prevents iOS Safari's rubber-band scroll bleed under
modals, which is otherwise jarring. The lock state is exposed as a
[data-scroll-lock] attribute on <html> for any
adjacent CSS that needs to react.
Editorial model
Main article: Talk page conventions
1Context's editorial model is Wikipedia's flat namespace plus
LKML-flavored talk pages, lightly adapted. Every article lives
at the URL root — no shelves, no books, no chapters. Article-to-article
structure emerges from content links, not from the filesystem: a section
summarizing a topic that has its own dedicated page opens with a
Wikipedia-style Main article: hatnote, and lateral pointers
collect in a See also section at the bottom. Anyone can
promote any page to "main article" status for a section just by writing
the hatnote — no central registry, no parent-child bookkeeping. And
because every web-aware AI agent has been trained on millions of
Wikipedia articles, the conventions are zero-teach:
Main article: and See also are recognized
instantly and followed the way Wikipedia intends.
Beside each article sits an optional sibling talk page
at pages/{slug}.talk.md (rendered themed at
/talk.html?page={slug}) where humans and agents discuss
improvements to the article. The format borrows everything Wikipedia's
twenty-year-old discussion culture demonstrably gets right: a
talk-header banner framing the page as discussion-not-forum;
descriptive H2 headings, one per topic; threaded replies (markdown
blockquotes in place of Wikipedia's colon indents, because blockquotes
render natively everywhere); signed posts with
— *author · ISO-timestamp*; inline status markers (bolded
**Done.** or **Fixed in PR #14.** in place of
{{done}}); a collapsible <details>
closure box on settled threads, modeled directly on the green
{{archive top}} boxes seen on mature talk archives like
Climate change Archive 83; and archive discipline so the live page
stays scannable.
The one notable divergence from Wikipedia is at the heading line: the
talk-page guidelines (WP:TPG) explicitly discourage bracketed prefixes;
we adopt them, in the LKML mailing-list style — [QUESTION],
[PROPOSAL], [BUG], [TODO],
[DECIDED], [RFC] — because intent
classification at the heading line is load-bearing for agents skimming
a talk file through grep. The trade is descriptive prose for
grep-affinity, a kernel-list win we'd rather have than not. Topic
state (open / closed / blocked) is derived from LKML-style
trailers (Closes:, Decided-by:,
Blocked-on:) at the bottom of the post, so the rendered
status badge can't drift from the linked PR or commit. The full
format spec, the Wikipedia and LKML lineage, worked examples, and
anti-patterns live in
Talk page conventions; the brief
one-page reference is at
talk-page-format.md.
Agent UX (AX)
Main article: Agent UX (AX)
1Context treats the AI agent as a first-class audience alongside the human
reader. The discipline of designing the website surfaces an agent encounters —
format on the wire, discoverability, lossless handoff — is what we call
Agent UX, abbreviated AX by analogy with UX.
It is a deliberate stack of cheap, redundant signals: the canonical
/page.md URL convention, an HTTP Link response header
(planned), a <link rel="alternate"> in HTML head, an
informational note placed after the lead paragraph, frontmatter
alternate_formats metadata in the markdown response, a Reader/Agent
toggle in the page header that lets a human inspect what the agent receives,
forthcoming llms.txt + llms-full.txt at site root
(mirroring OpenAI's developer docs), and an MCP server for lossless
agent-to-agent handoff (planned, separate project).
A controlled experiment with four parallel Opus subagents fetching four format variants of the same article established empirically that Claude's WebFetch summarizes every response down to ~300 tokens regardless of source format — meaning the URL stack's biggest win is not for summarizing agents but for the raw-fetch and typed-protocol classes (Claude Code in Bash mode, Cursor terminal mode, RAG pipelines, MCP-aware agents). The stack still raises the floor for everyone; only MCP solves the lossless-handoff ceiling.
For the full philosophy, the design principles, the per-layer status, the anti-patterns, and the open decisions, see the dedicated wiki article: Agent UX (AX).
Quality and testing
The repository ships four standalone Playwright-based verification scripts under
tools/, each guarding a different invariant or interaction class.
They're not in CI yet — they run on demand against the local dev server — but
the pattern is established for when CI is wired up.
Parent-header invariant
tools/audit-parent-header-invariant.py exercises every overlay state
(default, drawer-open, AI-bubble-open, AI-panel-open, customizer-open,
search-modal-open, bookmarks-modal-open, scrolled-deep, drawer-open-scrolled,
AI-panel-scrolled) at every viewport (375 / 414 / 768 / 1024 / 1440), 50 cases
total. The single rule: drawer-class UIs (persistent affordances) must NEVER cover
the parent 1Context header. Modal-class UIs (focused-task overlays) are allowed
to. The audit found 12 drawer-class violations on first run, all of which were
fixed in the same commit. Current state: 38 drawer cases pass, 0 violations,
12 modal covers (expected by design).
State-transition sweep
Earlier in the project a sweep across viewports × theme settings × UI states caught state-transition bugs that single-config tests missed: the TOC-hidden + narrow-viewport CSS specificity collision that squished the article into a sliver, the hamburger that disappeared at the bottom of long articles, the horizontal scrollbar from Wikipedia infobox tables overflowing the viewport, and the AI fixed-panel mode squishing the article at 1024px. All four were fixed.
Mobile touch test (Tier 2+3)
tools/verify-mobile-touch.py uses Playwright device emulation
combined with the matching browser engine — iPhone 13 with WebKit, Pixel 5 with
Chromium, iPad Pro 11 with WebKit — to exercise real touch interactions
(page.tap, page.touchscreen.tap) and a battery of
mobile-specific assertions: (hover: hover) reports false on touch,
drawer opens on tap, scrim tap closes drawer, TOC link tap auto-closes drawer,
no :hover sticks after tap-elsewhere, parent header stays visible
across all states, view toggle works under touch, modals open and close on tap,
touch targets are ≥ 36×36, the page survives mid-state rotation and dark theme,
body scroll lock activates and releases cleanly. 117 assertions per environment
across 13 categories of behavior. The first run caught five real WebKit-only
bugs that pure viewport-resize testing had been silently passing for hours.
Agent-format experiment
wiki-engine/tools/build-format-experiment.py generates five format
variants of the same article and serves them via the dev server through a
Cloudflare tunnel so that real Claude WebFetch instances (called by parallel
Opus subagents) can fetch them. The harness records what each agent received,
asked them to extract a specific buried test fact, and aggregated the results.
This was the experiment that surfaced the summarizer-bottleneck finding above
and reshaped the agent-surface priority order.
Roadmap
Near-term: Phase 6 (font selectors — heading and body, two options each, Radix-style
listbox UI mapped to data-font-heading / data-font-body
attributes). Phase 7 follow-ups: HTTP Link response header, the
Sec-Fetch-Dest server-side heuristic, an llms.txt at site
root, a "Copy markdown link" share menu, optional decision on whether the
Reader/Agent toggle should navigate to /page.md (URL-as-truth) once
the .md endpoint can do content negotiation. Medium-term: Phase 8
BookStack production wiring (real /api/search, the
/api/peek/{book}/{page} proxy for hover previews, AI chat against
the broker's POST /chat). Separate-track: the MCP server, scoped as
its own near-term project rather than as part of the theme.
References
agent-ui.md— full design rationale for the agent surface, six layered tactics with implementation status table.THEME_PHASES.md— phase plan with completion notes, probe-debt log, state-transition test matrix, and the BookStack production wiring reference.- BookStack — the underlying wiki engine.
- WikiWand — the visual and interaction reference for the human-facing layer.
- Model Context Protocol — the typed-tools interface 1Context will eventually expose for lossless agent handoff.