Skip to content

How Arkivel is built.

A fixed core, one generic data engine, first-party modules, and presets that combine them. This is the technical tour; the docs cover installation.

Core

The core is not a module and cannot be turned off. It is what every Arkivel instance is: a Next.js server talking to one PostgreSQL database through Prisma.

articleswiki linksbacklinksspacestagssearchrevisionsuserssessionsaudit logadminsettings

Articles are Tiptap documents with wiki links. Links are stored as rows, so backlinks, the graph, and redirects are queries rather than parsing. Every save writes a revision with diff, blame, and restore. The ⌘K palette searches pages, jumps to sections, and switches theme or skin.

Collections engine

Three tables cover tasks, reading lists, course trackers, CRMs, and a film library. A collection carries a typed property schema; items store values validated against it; views are saved queries over the items. An item may also be a real article.

Collection      { slug, name, categoryId?, schema: PropertyDefinition[] }
CollectionItem  { collectionId, articleId?, title, properties: Json, sortOrder }
CollectionView  { collectionId, slug, kind, config: ViewConfig, isDefault }

ViewConfig = { filters: Filter[], sorts: Sort[], groupBy?: propertyId, visible: propertyId[] }

Property types:

titletextnumberselectmulti_selectdatecheckboxurlpersonpagerelationcreated_timeupdated_time

View kinds: table, board, list, calendar. Unknown keys are dropped and wrong types rejected on every write. Relation writes check that targets exist in the bound collection, and item patches lock the row before merging so concurrent edits keep unrelated fields.

Modules

Modules are Arkivel's plugin system, kept first-party: no marketplace, no third-party code. Each one is a folder that declares what it owns. The sidebar, palette, help page, and feature list are composed from the registry, never hand-listed.

type ModuleDefinition = {
  id: ModuleId;            // "graph" | "assets" | "import" | …
  name: string;
  description: string;
  routes: string[];        // owned path prefixes; disabled → 404
  nav: NavEntry[];         // sidebar entries by section and order
  commands: PaletteCommand[]; // ⌘K entries, optionally role-gated
  docs: { help?: string; features: string[] };
  defaultEnabled: boolean; // used when ARKIVEL_MODULES is unset
};

Resolution, per request and cached: the ARKIVEL_MODULES comma list (or each module's default), then the admin override stored from /admin/modules. Pages call requireModule(id) and API routes return 404 when disabled. A module never imports another module; route handlers and kits are where they compose.

ModuleOwnsDefault
collections/collections/api/collectionsOn
graph/graph/api/graphOn
assets/assets/api/assets/api/uploadOn
import/import/api/import/api/articles/importOn
export/export/api/exportOn
api/api/v1/api-docsOn
feeds/feed.xml/feed/atomOn
share/share/api/articles/*/share-tokenOn
media/discover/api/mediaOff

Starter kits

A kit is { modules, skin, seed }. ARKIVEL_KIT picks one for a fresh database; /admin/kits applies a kit's seed later. Applying is idempotent by collection slug and never overwrites existing collections. Nothing in a kit is code.

KitModulesSkinSeeds
documentationcollections, api, assets, import, exporteditorialDocumentation collection: version, section, topic key, reading order
wikigraph, api, feeds, sharewikiNothing; the plain knowledge base
notes-and-taskscollections, graphfolioTasks (table + board by status) and a reading list
team-knowledge-baseEvery modulefolioAn empty tasks collection
course-workspacecollections, graph, import, exportfolioLinked courses and coursework with course-sync imports
watchlistcollections, media, assets, export, importfolioWatchlist and episodes collections from a starter catalogue

Site modes

A site mode is a shell, meaning pages and presentation, over the shared backend. The built-in modes below are examples, not a fixed set. A mode is chosen at build time with ARKIVEL_SITE_MODE, and each instance keeps its own domain, environment, database, and storage credentials.

Built-in modeWhat it is
wikiA knowledge base: articles, wiki links, backlinks, spaces, tags, search, and a link graph.
docsA versioned reading index over published articles, grouped by section with previous and next links.
mediaA film and series library on the same backend, database, accounts, and admin as the wiki.

Media mode is the worked example of Arkivel serving a domain that is not a wiki. The watchlist kit seeds two collection templates, the media module adds TMDB metadata with a bundled catalogue fallback, and src/media/ adapts collection rows into the library the shell reads. Mood picks use Claude only when ANTHROPIC_API_KEY is set and otherwise match locally.

Skins

Skins change layout, typography, density, and component presentation. Editors, permissions, and collection behaviour stay shared. The request resolves the reader's cookie, then their saved preference, then NEXT_PUBLIC_ARKIVEL_SKIN. Light and dark themes are separate from skins.

folio
The default: lowercase, quiet, written in place with a / block menu.
wiki
The classic encyclopedia layout.
editorial
Warm paper tones and serif reading typography.
compact
A denser workspace with a narrow sidebar and tighter tables.

Identity and access

Arkivel owns its users, roles (viewer, editor, admin), and sessions in its own database. Local passwords and optional GitHub or Google OAuth both end in an ordinary revocable database session; role changes apply immediately.

The first owner is created with npm run setup:owner under a PostgreSQL lock. ARKIVEL_REGISTRATION=closed is the default, and opening it only ever creates viewers. ARKIVEL_ACCESS=private puts pages, APIs, search, feeds, and files behind sign-in and fails closed on a database error.

Personal access tokens (Authorization: Bearer ark_…) make every /api route usable from scripts with the token owner's role. They are created and revoked under /settings/tokens and logged to the audit trail. See the API reference.

Storage

Uploads go to local disk (the default), any S3-compatible bucket, or legacy Vercel Blob. Local and S3 files are served through /api/files/… and pass instance authorization; non-image types download as attachments with sandbox and no-sniff headers. Stable object keys let you move from disk to a bucket without rewriting URLs.

ARKIVEL_STORAGE=s3
ARKIVEL_S3_BUCKET=arkivel-uploads
ARKIVEL_S3_REGION=eu-west-1
ARKIVEL_S3_ENDPOINT=https://…   # optional, any S3-compatible service

Operations

Schema changes ship as reviewed Prisma migrations. Builds never migrate production; containers apply pending migrations on start and other hosts run npm run db:deploy. npm run backup writes a PostgreSQL dump, uploads, table counts, and SHA-256 checksums, with a guarded restore rehearsal.

Admins get health endpoints, maintenance mode, read-only mode, an audit log, and Markdown, JSON, and ZIP exports. The installation guide and the private self-hosting guide cover the details.