Skip to content

Verified Record of Events and Knowledge

Done is a claim.
Verified is a fact.

Vrek is an API-first system of record for humans and agents doing work together — planning it, deciding it, building it and evidencing it. One graph, one API, provenance on every node.

Not two systems bolted together

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 — are the same objects, reachable through one API, with provenance and freshness as properties of every node.

Software is the first domain, not the only one. Requirements gathering and analysis run the same loop, and what an agent is told on connect belongs to the workspace rather than to a file in a repository.

A question, not a guess
When an agent would otherwise guess, it asks — and the work leaves the ready set until a human answers.
Evidence, not assertion
A status is a claim. A verification records who checked it, when, and how you would re-check.
Documents as views
A doc is blocks, not prose. Node and query blocks resolve on read, so they cannot go stale.
Append-only
Nothing is edited or deleted. A correction is a new event, and fsck re-verifies the whole chain.

The loop

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.

the working loop
# what can be picked up, with each item's footprint
$ vrek ready
$ vrek status iss-3bg971s in_progress
$ 
# would you have guessed? ask instead — this blocks the work
$ 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 iss-3bg971s --owner mike
claim, then evidence the claim
# a claim
$ vrek status iss-3bg971s done
$ 
# the fact behind it
$ vrek verify iss-3bg971s "integration suite passes" \
$     --result pass --evidence "npm test, 916 tests"
$ 
# shipped / blocked on you / claimed / ready
$ vrek report

pending is honest

A check written but never executed is --result pending, never pass. Resolve it afterwards; correcting a result appends a new event naming who corrected it, and the original stays in the log.

deferred ≠ blocked

Work put off on purpose is vrek defer with a wake date and a reason. blocked asserts something is in the way, which is a different fact.

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: altering any ancestor changes every descendant.

fsck reports three different things and does not confuse them. A hash mismatch is tampering. A missing parent or orphaned actor is a broken reference. Unchained means an event predates hashing and cannot be checked at all — unverifiable is not a failure, corruption is.

verify the recorded history
$ vrek fsck
chain intact: every hash recomputes and every parent resolves
$ 
# the projection rebuilds from the log, so it is not the record
$ vrek init && vrek import .vrek/log.ndjson

One core, five surfaces

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.

SurfaceEntry point
CLIvrek
MCP over stdiovrek-mcp
HTTPvrek-api
MCP over HTTPPOST /mcp
MCP resourcesvrek://doc/<id>

Over stdio the actor comes from VREK_ACTOR, so it is asserted rather than authenticated — fine for one trusted writer, and it fails silently the moment that stops being true. For anything shared, MCP over HTTP with bearer tokens is the transport, and an unauthenticated call is refused rather than quietly attributed to somebody.

Install

Requires node v24.0.0 and above. No other dependencies for the runtime.

a new workspace
$ npm install -g @mmainguy/vrek

Start a workspace

start using in a project
$ cd ~/some-directory
$ vrek init
$ vrek name "My Project"
# writes .mcp.json so an agent connects
$ vrek register --model claude-opus-5
$ vrek add "the first thing"
$ vrek ready
Use with a preexisting vrek-ified project
$ vrek init
$ vrek import .vrek/log.ndjson
$ vrek report
$ michaelmainguy@macbookpro flowstate % vrek report
$ vrek — 2026-09-16
$ shipped, verified (192)
$   ● iss-ekza8d2  Event-sourced core: append-only log with a rebuildable projection
$   ● iss-tsxa8bz  Principals: humans and agents as one table, on every event
$   ● iss-yhjp7y8  Ready set: unblocked work with each item's footprint
$   ● iss-0z6pvy5  Questions as objects: options, consequences, owner, revisit date
$   ● iss-kq5j6ka  Typed verification: claims and evidence in different columns
$   ● iss-g7rc93j  CLI over the core
$   ● iss-k5kg1vh  MCP server over stdio, no SDK dependency
$   ● iss-vjgpnqt  HTTP API with an SSE event stream
$   ● iss-sn9q9c9  Log export and import as newline-delimited JSON
$   ● iss-c18fmth  Docs as first-class nodes with owner and verified-at
$   ● iss-y767bat  Read-only stakeholder surface: shipped, in progress, blocked on you, drift
$   ● iss-kpwaq4k  Deferred work has no status: 'blocked' overloads blocker edges
$   ● iss-8eavsnh  Deferred work resurfaces when its wake date arrives
$   ● iss-bvzzrjd  Principle node kind, with guides and violates edges
$   ● iss-6j6rvys  Expose deferral and principles on CLI, MCP and HTTP
$   ● iss-1wkzy3c  Seed the brief's eight principles as principle nodes
$   ● iss-c8pn462  Expose updateNode on all three surfaces
$   ● iss-7a5nqp4  MCP over HTTP, so an agent needs no checkout and no terminal
$   ● iss-0qn1grr  Per-workspace instructions, carried over the protocol
$   ● iss-sekvvpb  Tool descriptions and touches assume a codebase
$ 
$ in progress (3)
$   ◐ iss-mj63a4f  Model reliability and capacity on flows: failure rate and call count, composed through nesting, groups and variants
$   ◐ iss-s6nf8z5  Draw the write path as a flow: an agent adds an issue through MCP down to the event log
$   ◐ iss-zbs8dxw  Build and deploy the vrek site on Cloudflare Workers
$ 
$ blocked on a human (7)
$   ○ que-pkp6v0h  I like this idea, but I think we should research and weigh against principles and goals
$       holds up ○ Immutability should be a per-kind policy the workspace can state (iss-c1wt4hk)
$   ○ que-7fj2r9y  Before the repository is shared, is the email in the log's history rewritten out once, or kept as a historical record?
$       options: Rewrite once before sharing: log and git history · Rewrite the log only, leave git history · Keep it as history
$       holds up ○ Share the repository beyond its maintainer: make the repo public, or hand a client a clone (iss-dpea1gn)
$   ○ que-yy2g6ss  How does a capability that needs a dependency attach to vrek without entering the core?
$       options: In-process plugins the core loads · Separate packages over the Workspace API · Sidecar processes that consume the event log · Decide per case; no general mechanism yet
$       holds up ○ Real identity: OIDC and scoped per-principal tokens (iss-jqeb2cs)
$   ○ que-rhzbxjm  Does que-thrdjv8's answer (the browser holds an OIDC token from a PKCE exchange) still stand now that OIDC is being designed?
$       options: It stands: the SPA holds the token · Reopen: the server does the exchange and keeps the cookie · Reopen: an authenticating proxy handles the browser flow
$       holds up ○ Real identity: OIDC and scoped per-principal tokens (iss-jqeb2cs)
$   ○ que-8zbk883  Where is vrek's Entra app registered: single-tenant in the operator's tenant with client users as guests, or multi-tenant with client users consenting in their own tenant?
$       options: Single-tenant in the operator's tenant; client users are B2B guests · Multi-tenant in the operator's tenant; users consent at home · Registered in the client's own tenant (the client self-hosts) · Support 1 and 3 now, 2 later
$       holds up ○ Real identity: OIDC and scoped per-principal tokens (iss-jqeb2cs)
$       holds up ○ What credential does vrek-api accept from an Entra sign-in: an access token for a vrek API scope, or the ID token from the sign-in scopes alone? (que-x67nqqd)
$   ○ que-v9wcr5t  Where does an idea or concept live before it is work: a question, an issue with a tag, or a new idea kind?
$       options: A question (recommended first step) · An issue carrying a stage:idea tag · A new idea kind
$   ○ que-x0b1874  What places a goal or item on the roadmap: a horizon tag facet, dates, or a revisit date?
$       options: A horizon: tag facet on goals (and on issues that need it), rendered by a live doc (recommended) · Start and target dates as attrs on goals · The revisit date already on deferred work
$ 
$ claimed done, no evidence (2)
$   ● iss-x3m5kc7  Drift from event seq, and acknowledgement as one action
$       failing ✗ drift over a path footprint is evaluable at all (ver-7zkzm9v)
$   ● iss-txyswdj  The console's node page has no section for what a node blocks, so `blocks` falls into the raw JSON fold beside guides, upholds, informs and informed_by
$       pending … A node's page shows a 'Holds up' section listing what it blocks (the server's `blocks`, or the log's blocks edges from an older server), and `blocks` no longer falls into the raw 'Everything else' fold (ver-xfe64jr)
$ 
$ deferred (3)
$   ⏸ iss-5rdvcqe  Git projection: render the workspace into the repo, and ingest edits back  (wakes 2027-01-01)
$       because dec-dn35d6q — deferred to v2; revisit when a second repo or a second machine appears
$   ⏸ iss-6v09kmm  Erasure and retention mechanisms: anonymize, redact, scrub  (no wake date)
$       because que-amf7dwd — not a feature; see the five reopen triggers on this issue
$   ⏸ iss-eas3dkp  .npmrc is tracked in git and carries the registry auth token  (wakes 2026-09-25)
$       because User deferred it on 2026-09-11 to keep working on other things
$ 
$ not yet answerable, waiting on work (1)
$   ○ que-x67nqqd  What credential does vrek-api accept from an Entra sign-in: an access token for a vrek API scope, or the ID token from the sign-in scopes alone?
$       blocked by ○ Where is vrek's Entra app registered: single-tenant in the operator's tenant with client users as guests, or multi-tenant with client users consenting in their own tenant? (que-8zbk883)
$ 
$ principles violated (0)
$ 
$ 
$ status not shown above (0)
$ 
$ 
$ goals retired (1)
$   MET         × gol-t8fyeh8  Five small fixes like iss-z3dcqzw ship in the next build/publish cycle (0.2.24)
$ 
$ ready to pick up (62)
$   ○ iss-3qhhg79  register's source form still writes an absolute VREK_DIR when the checkout is outside the workspace
$       touches src/cli/main.ts, test/cli.setup.test.ts
$   ○ iss-htadfvt  Regenerate and commit this repo's .mcp.json, which still names the absolute VREK_DIR that register no longer writes
$       touches .mcp.json
$   ○ iss-wcw03cq  Reading a flow whose stored steps or branch are malformed throws instead of reporting it: flowDiagram, flowBudget and the cycle check all crash
$       touches src/core/workspace.ts, src/domain/flowDiagram.ts
$   ○ iss-hj90dm0  Changing only a variant's branch does not re-check its stored steps, so a reply that answered an inherited call is left answering nothing
$       touches src/core/workspace.ts
$   ○ iss-e6y0cxf  flowGroupProblem counts a blank " label as a label but an unset one as none, so "" then "x" in a loop is refused while unset then \"x\" is allowed
$       touches src/domain/types.ts
$   ○ iss-f9rqqa8  flowGroupProblem does not require a group's steps to agree on its kind, so one group id can be a loop on one step and a par or opt on the next
$       touches src/domain/types.ts, test/workspace.test.ts
$   ○ iss-qypc2ys  Signing in under `npm run ui` is refused as 403 "origin not allowed": the dev proxy rewrites Host but the browser's Origin stays the Vite origin
$       touches src/api/http.ts, vite.ui.config.ts
$   ○ iss-jpkfjf6  Relation lists (blockers, blocks, guides, informs…) come back sorted by target id, which reads as random, not in any meaningful order
$       touches src/core/workspace.ts
$   ○ iss-47c1mpp  The console reports any failed capability probe as "does not implement", so an auth failure, a 5xx or an unreachable API reads as a missing route
$       touches src/ui/App.svelte, src/ui/lib/Missing.svelte, src/ui/lib/api.ts, src/ui/lib/store.svelte.ts
$   ○ iss-v4ft299  A verification attached to the wrong issue cannot be re-targeted, so a misfile permanently overstates that issue's evidence
$       touches src/api/http.ts, src/cli/main.ts, src/core/workspace.ts, src/mcp/tools.ts, src/store/log.ts
$   ○ iss-z7dgbst  vrek edit cannot correct 'touches', so a typo in the one field ready() reads is permanent
$       touches src/api/http.ts, src/cli/main.ts, src/core/workspace.ts, src/mcp/tools.ts
$   ○ iss-pnrhzqp  Console: a Findings page and a NewFinding form
$       touches src/ui/App.svelte, src/ui/lib/NewFinding.svelte, src/ui/lib/store.svelte.ts, src/ui/routes/Findings.svelte, src/ui/routes/NodeDetail.svelte
$   ○ iss-pmqcwmp  Research is work whose outcome is a document, and nothing marks it as such
$       touches src/cli/render.ts, src/core/workspace.ts
$   ○ iss-avxbtcb  A dependency listing repeats itself: 13 lines for 4 dependencies at 7 components
$       touches src/cli/render.ts, src/core/workspace.ts
$   ○ iss-mhdn39x  The per-project alias plan cannot work through .envrc: direnv sets env vars, not aliases
$       touches src/cli/main.ts
$   ○ iss-5zv89ns  Coverage cannot see the CLI tests, so the reported number understates by ~850 lines
$       touches vitest.config.ts
$   ○ iss-vy4aed6  acknowledged_at_seq is a local number, so drift breaks after a merge
$       touches src/core/workspace.ts
$   ○ iss-mn01p4h  A byte-level scrub does not propagate between workspaces
$       touches src/store/log.ts
$   ○ iss-z0ytgwf  nodes.owner holds three incompatible shapes, defeating any pseudonymisation
$       touches src/cli/main.ts, src/core/workspace.ts
$   ○ iss-mwjfsf2  Nothing ever compares two footprints, so ready() sells a decision procedure it does not implement
$       touches src/core/workspace.ts

That second block is the step a clone always needs and the one that is easy to miss: every command answers no Vrek workspace found until you import. Export after any session that changed the workspace, commit the log beside the code, and it travels.

It tracks its own development

.vrek/ in the Vrek repository is a live workspace, not a demo fixture. These numbers were read out of it on 15 September 2026.

3,640
events in the log
hash-chained, none rewritten
194
issues done
74 open, 2 in progress
321
verifications passing
16 pending, 1 failing
7
principals
humans and agents, same table

Not built

  • Real identity beyond static bearer tokens
  • Postgres for a shared server
  • Three-way merge on import
  • Erasure mechanisms
  • The git projection, deferred to 2027

Known limitations

Because a guarantee should say where it stops holding.

  • importLog is not atomic
  • attrs cannot be written or queried from any surface
  • docDrift is blind to evidence and to path footprints
  • ready() does a full scan, and is the first latency cliff

All of them are recorded in the workspace — vrek ready lists them with the rest, rather than leaving them in prose nobody queries.