Skip to content

From Innerpedia, the encyclopedia of you

innernet is a TypeScript Next.js application within apps, part of euler. Created in September 2026, it was last touched 3 weeks ago. It holds 317 files across 53 folders, 98 MB in all. Of its folders, seven lie past the six levels Innerpedia reads: their files are counted, but they have no pages of their own.

Innernet turns the folders on your machine into a small, connected web. Find a project by its name, language, framework or README text, then open its Innerpedia article to see what it does, how it is organised and how it has changed.

Contents
  1. (Top)
  2. Overview
  3. The public demo (Vercel)
  4. How it works
  5. Get started
  6. Search your folders
  7. History
  8. The database
  9. Structure
  10. Technology
  11. See also
  12. External links

Overview

Your existing folders are the source material. One local JSON index powers both readers.

ExploreWhat you will find
Search · / and /searchA familiar search box, highlighted results, live suggestions, spelling corrections and project knowledge panels.
Innerpedia · /wikiProject articles with README overviews, folder trees, technology, Git history and related projects. Browse categories, statistics or a random article.
Field guide · /#guideThe second half of the home page: an illustrated walkthrough of the crawl, search and privacy rules, an interactive recipe for turning a folder into an article, and a prompt that hands Innernet to an AI assistant.
History · /activityEvery page you opened, one session per browser tab, kept as plain files on this machine with a copy in its own database. The header's arrows step back and forward.

The public demo (Vercel)

The same app runs as a public demo at https://innernet-nine.vercel.app. Instead of this machine, it indexes the open-source repositories of github.com/quirq-ai:

pnpm index:demo   # clone the public quirq-ai repos (anonymous HTTPS) and build data/demo/index.json
pnpm dev:demo     # preview the demo locally

data/demo/index.json is committed, and every path in it is a GitHub URL. Vercel builds always run the demo (VERCEL=1); locally INNERNET_DEMO=1 switches it on (lib/mode.ts). In the demo the localhost guard is off, a banner says what you are looking at, folders link to GitHub instead of VS Code, and the guide plays the committed 720p film. With a database it keeps the pages and searches visitors open for 30 days, anonymously (the demo's history); without one, history stays in the browser.

How it works

flowchart LR
    folders["Your folders"] --> crawler["Crawler<br>READMEs, manifests, Git metadata"]
    crawler --> index["Local index<br>data/index.json"]
    index --> search["Search<br>Find a project"]
    index --> wiki["Innerpedia<br>Read its story"]

The crawler records folder structure, project descriptions, languages, dependencies and Git history. Innerpedia assembles articles from that information; folders with less project context become stubs. Shared names get disambiguation pages, and categories connect projects across your directory tree.

Search runs on the server with MiniSearch. The app uses Next.js, React, TypeScript and Tailwind CSS, with Markdown rendered by react-markdown and remark-gfm.

Get started

You need Node.js 20.9 or newer and pnpm. Run the commands from the repository directory so the server can find its index.

1. Install

git clone https://github.com/quirq-ai/innernet.git
cd innernet
pnpm install

2. Choose your folders

Edit innernet.config.json before indexing. The defaults are:

{
  "roots": ["~/Programming"],
  "maxDepth": 6
}

Add or replace roots with the folders you want to explore. maxDepth controls how many levels of folders get their own pages.

3. Index and open

pnpm index
pnpm dev

Open localhost:3470, search for a familiar project, or visit Innerpedia and the field guide. Both pnpm dev and pnpm start bind to 127.0.0.1.

The first dev run or build needs network access to fetch fonts. Afterward, the fonts are served locally.

Keep the index fresh

Run pnpm index after your folders change. The server picks up the new index on the next request without a restart.

For a single run, override the configured roots and depth:

INNERNET_ROOTS="~/projects,~/notes" INNERNET_MAX_DEPTH=3 pnpm index

To inspect a trial index without replacing the app's index:

INNERNET_ROOTS="~/projects" INNERNET_OUT=/tmp/innernet-test.json pnpm index

The app always reads data/index.json from its working directory; INNERNET_OUT only changes where the crawler writes.

Search your folders

Start with ordinary words, then narrow the results with operators. Search covers project names, paths, descriptions, README text, dependencies and agent notes.

TryFinds
lang:rustFolders whose leading languages include Rust.
in:experimentsFolders inside a matching ancestor folder.
kind:repoGit repositories.
fw:nextProjects using Next.js.
is:articleInnerpedia articles.
is:stubFolders with less project context.

Combine them with free text, such as chat lang:ts fw:next. Quote values containing spaces, such as in:"side projects". Results can also be filtered using the Projects, Repositories, Documents and Folders tabs.

History

Innernet writes down what you open, in plain files on this machine. The files are the record and the format every app shares, with no index file and no schema registry: a session is a folder, and each app that takes part writes its own JSON Lines file inside it. The database keeps a copy of the lines, read in as they change.

~/.innernet/history/                  set INNERNET_HISTORY_DIR to keep it elsewhere
  2026-10-03T05-12-07Z_k3f9a2/        one folder per browser tab: its start time (UTC) and a short id
    innernet.jsonl                    one JSON object per line, appended by Innernet
    <any-app>.jsonl                   any other app or tool adds its own file

Every line is {"at":"<ISO time>","app":"<name>","kind":"<verb>", ...} plus whatever fields the app likes. Innernet writes visit, search, back and forward:

{"at":"2026-10-03T05:12:09.512Z","app":"innernet","kind":"visit","url":"/wiki/galileo","title":"galileo"}
{"at":"2026-10-03T05:12:31.020Z","app":"innernet","kind":"search","q":"agent","url":"/search?q=agent"}

Reading a session means reading every *.jsonl in its folder and sorting by at. App names are lowercase letters, digits, - and _; lines over 4 KB, lines that are not JSON and lines without a valid at and kind are skipped. To add your own activity, append a line to a file named after your app. This joins the newest session:

echo '{"at":"'$(date -u +%FT%TZ)'","app":"notes","kind":"edit","file":"todo.md"}' >> "$(ls -d ~/.innernet/history/*/ | tail -1)notes.jsonl"

/activity, the clock in the header, shows the sessions newest first, each opening onto its events merged across apps. The header's arrows step back and forward through the pages of the current tab. The browser writes through the app's own /api/activity route, which accepts only same-origin requests on localhost, and browsers driven by automation are not recorded. The route appends the line to the file first, then reads that session's files into the database; /activity reads in every folder that changed before it shows the list, so a line another app appended shows on the next visit. Without the database the page reads the files, as it always has. Delete a session's folder to forget it; the database forgets it on its next read. The public demo keeps its visitors' history differently: see the demo's history.

The database

Innernet keeps a copy of what it serves in a small Postgres database. The files stay the source; the copy means a deleted index does not blank the site, the history page reads every app's lines with one query, and both can be written back out as files.

On this machine it is PGlite, Postgres compiled to WebAssembly, running inside the server on a folder of plain files. Nothing listens on a port and nothing leaves the machine.

~/.innernet/db/          set INNERNET_DB_DIR to keep it elsewhere, INNERNET_DB=off to go without
  pgdata/                the Postgres cluster (folder mode 700)
  owner.lock             the process that has it open: one at a time
TableHolds
kvSmall named values: the index's meta and disambiguation, the schema version, and one mark per history file (bytes read so far, inode, mtime, a hash of the bytes before the mark).
pagesOne row per page of the index, in the index's order.
activityOne row per history line: session, app, time, kind, the event as JSON and the exact line. Its key is a hash of the session, the app and the line, so a line stored twice is one row.

The server stores each new data/index.json as it loads it, and if the file goes missing it serves the stored index instead. The history comes in as it changes: after each event the recorder sends, and whenever /activity is read. A file whose size, inode and mtime match its mark is not opened; one that has grown is read from its mark on, new bytes only; one shortened, edited in place or replaced is read whole, and its rows become exactly its lines. A line the database will not take is skipped on its own, never holding back the rest. A session folder or app file you delete is forgotten in the database too. The history folder itself is different: the database notes which folder it read (its device, inode and birth time), and if it finds another one there (the folder was lost, moved or made anew), it keeps every session that did not come with it, however many new ones the new folder gains, so pnpm db:load can write them back. To forget everything at once, delete ~/.innernet/db as well.

PGlite runs Postgres single-user, with no autovacuum and no checkpointer, so Innernet vacuums a table and checkpoints after every few hundred rows rewritten or deleted (a re-index counts), and keeps the write-ahead log under 64 MB. The folder stays near 70 MB for an index of 5,000 pages, however often it is rebuilt.

One process opens the folder at a time: a second server works from the files alone and says so once, and the commands below decline politely while the server has it. A lock left by a server that crashed is taken over, even if its process id has since gone to another program; with no Innernet running, deleting owner.lock is always safe.

pnpm db:status   # where it lives, its size, what it holds
pnpm db:store    # store data/index.json and bring the history up to date with its folders
pnpm db:load     # write the stored index and history back out as files (stop the server first)

/activity has a Database card with the same counts and a Store now button, which runs what db:store runs inside the server that already holds the folder (a same-origin POST to /api/db/store, on localhost only). db:load adds the history lines that are missing and never deletes any, and it leaves an index file newer than the stored copy alone unless you pass --force.

VariableEffect
INNERNET_DB_DIRWhere the database lives (default ~/.innernet/db).
INNERNET_DB=offNo database: Innernet reads and writes its files alone.
INNERNET_HISTORY_DIRWhere the history folders live (default ~/.innernet/history).

The demo's database

The public demo uses Neon serverless Postgres instead, in US East, through the DATABASE_URL that the Vercel Marketplace sets. Local mode refuses to connect to it, or to any remote database, whatever is set. It holds two things.

The demo index. On first use, then at most every five minutes, the demo looks for an index newer than the bundled data/demo/index.json and serves it once it passes the same leak checks as pnpm index:demo. The server never writes it. To store a fresh one, with the connection string in the environment (the maintainers keep it in the gitignored .env.neon.local, which Next.js never loads):

pnpm index:demo
set -a; . ./.env.neon.local; set +a; pnpm db:store --demo

db:store --demo refuses a local index, and anything that names this machine, before a byte reaches Neon; the store itself refuses the same again, whoever calls it. Without --demo the commands drop DATABASE_URL from their environment and refuse to run while INNERNET_DEMO, INNERNET_DEMO_BUILD or VERCEL is set, so a shell left in demo mode cannot send this machine's index anywhere.

A build that can see DATABASE_URL turns off Turbopack's persistent cache (next.config.ts): that cache saves the whole environment of the build in .next/cache, in files anyone on the machine can read, and the connection string belongs only in .env.neon.local.

The demo's history

The recorder keeps each visitor's events in their browser's localStorage, and when the demo has a database it also posts them to /api/activity, which keeps them in the same activity table for 30 days:

  • What is stored: per event, the kind (visit, search, back or forward), the page's path on the demo (one of its own pages, with no query but the search's own q, t and p: campaign tags and other sites' click ids are dropped in the browser and again on the server), its title or the search words, via (whether the header's buttons or the browser's moved you back or forward), and the server's time. Each event is kept onc

Structure

Folders

8 subfolders

Files

12 files at the top level
  • CONTRIBUTING.md
  • DESIGN.md
  • innernet.config.json
  • instrumentation.ts
  • next.config.ts
  • package.json
  • pnpm-lock.yaml
  • postcss.config.mjs
  • project.json
  • proxy.ts
  • README.md
  • tsconfig.json

Technology

Languages

share of 211 files
TypeScript61%JavaScript23%JSON6.2%Markdown5.2%CSS1.9%Shell1.4%YAML<1%HTML<1%

Frameworks and libraries

Scripts

package.json
  • index
  • index:demo
  • db:status
  • db:store
  • db:load
  • dev
  • dev:demo
  • build
  • start
  • typecheck
  • test:mount

Dependencies

9 runtime8 development
  • @electric-sql/pglite
  • @neondatabase/serverless
  • minisearch
  • next
  • react
  • react-dom
  • react-markdown
  • remark-gfm
  • server-only
  • @tailwindcss/postcss
  • @types/node
  • @types/react
  • @types/react-dom
  • postcss
  • tailwindcss
  • tsx
  • typescript

See also