@mmainguy/vrek (0.2.1)
Installation
@mmainguy:registry=npm install @mmainguy/vrek@0.2.1"@mmainguy/vrek": "0.2.1"About this package
Vrek
An API-first system of record for humans and agents doing work together — planning it, deciding it, building it and evidencing it. Software is the first domain, not the only one: requirements gathering and analysis run the same loop.
Jira and Confluence are two systems of record-keeping bolted together. Vrek is one graph: the work, the knowledge, the decisions, the evidence and the principals — human or agent — who touched them are the same objects, reachable through one API, with provenance and freshness as properties of every node.
The reasoning behind it is in RESEARCH_BRIEF.md. The shape of the implementation is in docs/ARCHITECTURE.md.
Status
v0.1. Vrek tracks its own development: .vrek/ in this repository is
the live workspace, and every claim below is a verified issue in it.
630 events, 41 issues shipped with evidence, 22 ready to pick up, 7 questions waiting on a human. 161 tests; 64% of statements covered, which understates by about 850 lines because the CLI is tested as a subprocess and V8 cannot see it.
Working: the event log with causal parents and content hashes, the graph, the ready set with footprints, blocking questions, typed verification, principles with recorded violations, documents as views over facts with exact drift, and one core behind five surfaces.
Not built: real identity beyond static bearer tokens, Postgres for a shared server, three-way merge on import, erasure mechanisms, and any UI. The git projection is deferred to 2027.
Known limitations, since a guarantee should say where it stops holding:
importLog is not atomic; attrs cannot be written or queried from any surface,
which is a mechanism one principle depends on; docDrift is blind to evidence
and to path footprints; ready() does a full scan and is the first latency cliff.
All are recorded — vrek ready lists them with the rest.
Migrating a workspace named before the rename
The database does not change. Nothing in the schema, the event types or the event payloads carries the product name — only the directory does. So a workspace built under the old name migrates by moving a directory:
cd ~/your-project
mv .flowstate .vrek # or: git mv .flowstate .vrek
vrek fsck # the chain verifies unchanged
If only the committed log survived and the database is gone, rebuild it:
vrek init
vrek import .vrek/log.ndjson
Then repoint the agent config. .mcp.json needs three things changed: the
command becomes vrek-mcp, every FLOWSTATE_* environment variable becomes
VREK_*, and any path ending /.flowstate becomes /.vrek. Running
vrek register --force rewrites it for you.
Two things change that a shell will not warn you about, because they are
protocol-visible. Every MCP tool is now vrek_* rather than flowstate_*, and
document resources are vrek://doc/<id>. An agent holding an old connection
keeps calling the old names until it reconnects.
The event log is deliberately not rewritten. Every event carries a content
hash covering its payload, so editing historical text to say vrek would break
the chain on every event and vrek fsck would report the whole log as tampered.
The log keeps saying flowstate wherever it did, because that is what happened.
Install
npm install
npm run build # emits dist/
npm link # puts vrek, vrek-mcp and vrek-api on PATH
npm unlink -g vrek removes them. For working on Vrek itself there is
no build step — Node 24 runs the TypeScript directly, so node src/cli/main.ts
is always the current source.
Start a workspace
cd ~/some-project
vrek init # creates .vrek/
vrek name "My Project" # records the name in the log
vrek register --model claude-opus-5 # writes .mcp.json so an agent connects
vrek add "the first thing" --touches src/
vrek ready
vrek help prints this. register merges into an existing .mcp.json
rather than replacing it, refuses to overwrite an entry without --force, and
points at the checkout it was run from — so working on Vrek itself needs no
rebuild between edits.
To open this repository's own workspace after a clone:
vrek init
vrek import .vrek/log.ndjson
vrek report
init takes no name: the name travels in the log and the import supplies it.
.vrek/log.ndjson is the committed event log; .vrek/workspace.db is
a projection of it and is not committed. Export after any session that changed
the workspace: vrek export --out .vrek/log.ndjson. Export and import
also reach MCP (vrek_export, vrek_import) and HTTP
(GET /export, POST /import), so an agent can get the workspace out of the
machine without a shell — commit it beside the code and it travels.
The loop
vrek ready # what can be picked up, with each item's footprint
vrek status <id> in_progress
vrek ask "Postgres or SQLite for the shared server?" \
--option "postgres|a service to run on day one" \
--option "sqlite|single binary, one writer" \
--blocks <id> --owner mike --revisit 2026-09-19
vrek status <id> done # a claim
vrek verify <id> "integration suite passes" \
--result pass --evidence "npm test" # the evidence behind it
vrek report # shipped / blocked on you / claimed / ready
vrek status <id> done records that somebody said it was done. Until a
verification passes, vrek report and vrek unverified both show the
claim as a claim. That distinction is the point of the product.
A check can be registered before it is performed — --result pending is the
honest state for that — and resolved afterwards with
vrek resolve <ver-id> --result pass --evidence "…". Correcting the result
appends a new event naming who corrected it; the original stays in the log.
Work put off on purpose is vrek defer <id> --until DATE --because "why",
not blocked — blocked asserts something is in the way, and a deferred item
returns to the inbox on its wake date.
Documents are views over facts
A document is not a page of prose that rots. It is an ordered list of blocks:
vrek doc "Phase 1 scope"
vrek block doc-abc --text "Phase 1 covers EMEA only."
vrek block doc-abc --node dec-5kg0g6n # renders current state
vrek block doc-abc --query kind=question,status=open
vrek render doc-abc
vrek ack doc-abc # one action: you vouch for it as of now
vrek drift # documents something has moved under
node and query blocks resolve when the document is read, so they cannot go
out of date. Only what you write as text can later be wrong, which is the
point: the part that rots is reduced to the part a human wrote.
Acknowledging stamps the document with the current event seq. Drift is then any event on a covered node with a higher seq — exact rather than heuristic, and it names which node moved and when. A render reports the seq it resolved at, because a query block changes a document without any event on the document.
Every block records the principal who wrote it, and every renderer attributes it. A doc block is text one principal wrote that another principal's agent reads, so authored text reaches a model as a quoted claim by a named person rather than as an instruction from the system.
The log is checkable
Every event carries the events it was appended after and a hash over its canonical form, so the hash covers the history reaching it and altering any ancestor changes every descendant.
vrek fsck # recompute every hash, resolve every parent and actor
It reports three different things and does not confuse them: a hash mismatch is tampering, a missing parent or orphaned actor is a broken reference, and unchained means an event predates hashing and cannot be checked at all. Unverifiable is not a failure; corruption is.
Surfaces
| Surface | Entry point | For |
|---|---|---|
| CLI | vrek |
humans and shell-driven agents; --json on any command |
| MCP over stdio | vrek-mcp |
one trusted writer; see Choosing a transport |
| HTTP | vrek-api |
services and future UIs; SSE at /events/stream |
| MCP over HTTP | POST /mcp on that server |
agents with no checkout and no terminal |
| MCP resources | vrek://doc/<id> |
documents as agent-readable context |
All of them call the same Workspace class. A capability reachable from one and
not the others is a bug, and gets filed as one.
Choosing a transport
Over stdio the actor comes from VREK_ACTOR, so it is asserted rather
than authenticated: anyone who can start the process can claim any identity.
That is fine for one person who is the sole writer of their own workspace, and it
fails silently the moment that stops being true — a shared machine, a CI runner,
or a workspace several people write to.
So stdio is for a single trusted writer, and MCP over HTTP with bearer tokens is the transport for anything else:
VREK_PORT=7373 \
VREK_TOKENS=tok-mike:mike:human,tok-agent:claude:agent \
vrek-api
vrek register --http https://your-host:7373/mcp
export VREK_HTTP_TOKEN=tok-agent # in the agent's environment, never in the file
MCP is then at /mcp, and a call carries Authorization: Bearer tok-agent. The
.mcp.json entry holds ${VREK_HTTP_TOKEN} rather than the token, so the file
can be committed; the client expands it. An unauthenticated call is refused
with 401 rather than silently attributed to somebody.
The console on the same server signs in with any of the same tokens: paste one
into the form and it becomes an HttpOnly session cookie, gone when the browser
closes. Writes from the console are attributed to that token's handle.
The server binds loopback unauthenticated by default and refuses a non-loopback
bind without VREK_TOKENS. VREK_ALLOWED_ORIGINS=https://app.example.com,...
lists the origins a browser may call /mcp from; a request carrying any other
Origin is refused, and one carrying none (curl, an MCP client, a service) passes.
vrek-api --help lists every variable the server reads.
TLS
vrek-api speaks plain HTTP and expects TLS, when there is any, to be
terminated in front of it. Caddy does that in one line, including obtaining and
renewing a Let's Encrypt certificate:
caddy reverse-proxy --from vrek.example.com --to 127.0.0.1:7373
nginx with certbot works the same way. Whatever the proxy is, it must pass
X-Forwarded-Proto: https (Caddy does by default; nginx with
proxy_set_header X-Forwarded-Proto $scheme;), which is how the server knows to
mark the console's session cookie Secure.
For a private network where a proxy is one more thing to run, vrek-api can
serve https itself from a certificate it is handed:
openssl req -x509 -newkey rsa:2048 -nodes -days 365 \
-keyout key.pem -out cert.pem -subj "/CN=vrek.internal"
VREK_TLS_CERT=cert.pem VREK_TLS_KEY=key.pem VREK_TOKENS=... vrek-api
A self-signed certificate has to be trusted by every browser that opens the
console and by every agent that connects (NODE_EXTRA_CA_CERTS=cert.pem for a
Node-based MCP client), or the connection is refused. vrek-api never obtains or
renews a certificate: replace the files and restart. dec-23gfv9w records why.
Without a repository
Nothing in the core touches git. .vrek/ is a directory, not a checkout,
and the only git call in the codebase reads user.email as a fallback identity
in the CLI. A hosted workspace is a data directory and a server:
VREK_DIR=/srv/vrek VREK_HOST=0.0.0.0 \
VREK_TOKENS=tok-priya:priya:human,tok-agent:claude:agent \
vrek-api
An agent then connects to https://your-host/mcp — no clone, no Node, no
.mcp.json. Its calls are attributed to whichever token it presented, so "who
decided this" stays a query rather than an inference.
Instructions
What an agent is told on connect belongs to the workspace, not to a file in a repository that a non-git user does not have:
vrek instructions # what agents are told today
vrek instructions "This workspace is the Q4 requirements phase. \
Requirements are issues; a sign-off is a verification with the meeting \
that produced it as evidence."
The default names no codebase. The loop — ready, ask when you would guess, claim, then evidence the claim — is the same for requirements gathering, analysis and planning as it is for software. Instructions and the workspace name are both event-sourced, so both survive an export into a fresh workspace.
Identifying an agent
VREK_ACTOR=claude VREK_ACTOR_KIND=agent \
VREK_MODEL=claude-opus-5 VREK_SESSION=$SESSION_ID \
vrek ready
Every event records the principal, whether it was a human or an agent, and for
agents the model and session. "Who did this and why" is a query, and
vrek_principals answers "who else works here" from the agent side.
Dependencies
Development Dependencies
| ID | Version |
|---|---|
| @awesome.me/webawesome | ^3.12.0 |
| @sveltejs/vite-plugin-svelte | ^7.3.0 |
| @types/node | ^26.2.0 |
| @vitest/coverage-v8 | ^4.1.11 |
| svelte | ^5.57.0 |
| typescript | ^5.9.2 |
| vite | ^8.2.2 |
| vitest | ^4.1.10 |