Skip to content
View lacunari's full-sized avatar
🎯
Focusing
🎯
Focusing

Block or report lacunari

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
lacunari/README.md

Lacunari

A hundred workers. One project. Nothing collides.

Lacunari reads an entire project — source code, documents, scans, research — and turns it into a living roadmap: what exists, what is missing, and what to do next. Engineers, contractors and AI agents work it at the same time, on the same repository, without duplicating each other or overwriting each other's files.

The coordination is a Postgres database. There is no daemon, no broker, no scheduler to run and no vendor in the path. Anything that can reach the database is a peer: a Claude Code session, a GPT worker, a local Llama, a cron job, a contractor with psql.

lac doc index . --source my-repo        # read the whole project
lac doc extract && lac web build        # map it
lac web gaps                            # what is missing

  files 92 · 6 languages · 13,582 lines
  no docs mention   29      nothing calls    122
  nothing names it   2      no test mentions  86

lac web suggest --dispatch              # → 47 claimable tasks
lac work &  lac work &  lac work &      # and go to bed

How it works: ingest anything, map it, find the holes, file them as work, agents claim without colliding, finished work withdraws itself


Your version control was designed for humans typing. In 2005.

Git is superb at what it was built for: recording that bytes changed, and reconciling two people who changed them, at the speed people work. It has never read a file, has no idea what your project is, and detects a collision only once both parties have finished.

That was a reasonable set of assumptions when the whole team was human. It is not the team you have now.

Question Git Lacunari
What changed? line diffs between commits every version content-addressed, who contributed it, when, in an immutable audit trail
What is this codebase? stores your files, never reads them every symbol, import and language, and what calls what
What is unfinished? absence is not a diff undocumented, untested, uncalled, orphaned — already filed as claimable work
Who is on it right now? a merge conflict, after both did the work an atomic claim, before a keystroke is spent
What is this goal made of? decomposed into a tree, one level at a time, against the real files
Merging two parties' work conflicts when you touched the same lines cannot conflict — same bytes, same hash, so a merge is a union
What did it cost? dollars and human minutes, per task, with hard ceilings

Most teams run both, pointed at the same tree. See docs/VERSIONING.md for the full overlap.

The roadmap is derived, not maintained

A roadmap is normally a document somebody keeps up to date until they stop. Here it is derived from the project itself, expanded one level at a time against what is actually in the tree, and it updates itself as work lands.

lac goal add "Ship multi-tenant support" \
    --criteria "every query scoped to a tenant, with tests" \
    --depth 3 --budget 40.00

lac goal plan 1        # expand ONE level, grounded in the code

  ○ Ship multi-tenant support
    ○ Add tenant_id to the 9 tables that lack it        work
    ○ Scope the 41 queries in core/ that ignore it      work
    ○ Decide the isolation boundary: RLS or app-level   needs a human
    ○ Backfill fixtures for the 12 untested paths       work

lac work &  lac work &  lac work &

Planned one level at a time. The decomposition at depth 4 depends on what depth 2 learned, so leaves are expanded when they are reached, with their siblings' findings in hand. Work starts before planning ends, and a wrong branch is pruned before its subtree exists.

It grooms itself. Every derived item carries a stable identity, so when the work is genuinely done the item withdraws itself. No backlog triage, and no tickets describing a codebase that moved on six weeks ago.

Status without a status meeting. Rollups by subtree — open, claimed, blocked and shipped per epic or department, with the dollars and human minutes each one cost. The report is the database.

Judgement calls come back to you. A planner marks what it cannot decide and escalates rather than guessing confidently. Humans stay on the decisions, not the dispatching.

Every expansion records what the planner was shown and what it returned, so "why is this on the roadmap?" has an answer. Depth is capped, budget is checked before expanding, and malformed output is dropped rather than filed. Two of the three shipped planners need no model at all.

It reports what is not there

Every search engine, vector database and RAG pipeline answers one question: what in here matches? Ask any of them what you are missing and they return nothing — which is indistinguishable from a question with no answer.

 year | documents |    bar
------+-----------+------------
 1941 |         1 | █
 1942 |         1 | █
 1943 |         0 | ◻ MISSING
 1944 |         1 | █

Nobody typed that year in. Lacunari read the dates out of the documents, saw the span was otherwise covered, and reported the hole. Pointed at a repository the same query shape reads "29 files no documentation mentions, 122 symbols nothing calls."

Four shapes of absence, each a lead rather than a verdict:

a hole in a covered span you hold 1942 and 1944, so where is 1943
resting on one source nothing corroborates this — the reason to federate
connected to nothing named once and never placed: a dead end, or the loose thread
never read held but never extracted, so invisible to every question above

Each becomes a task with a title and a priority — and withdraws itself when someone closes it, so the board reflects the project rather than a backlog nobody trusts.

All of it offline: set-based SQL over data you already hold. No model, no API key, no network call. That makes results reproducible, and means they do not shift because a vendor shipped a new checkpoint.

A hundred workers, no collisions

lac work &   lac work &   lac work &   lac work &

Each worker asks the board for the top task it is capable of, claims it, declares the files it will touch, does the work, records what it cost, and asks again. They do not step on each other, for structural reasons:

  • A claim is one atomic UPDATE. Two workers cannot take the same task.
  • A claim locks the files that task declared. The second worker is told "dana holds core/auth.py on task #12" rather than discovering it at merge time. Readers coexist; writers are exclusive.
  • A worker that dies loses its lease. Its files unlock and the task returns to the board with its history intact.
  • Capabilities route the work. The agent with production credentials gets the deploy task; the one without never sees it.
  • Judgement calls escalate to a human on a deduplicated backoff, because an agent that cries wolf trains people to ignore it.

For teams, identity is the connected Postgres role, lane permissions are row-level security, and the audit log is append-only in the database — so a contractor and a model are governed identically. See docs/TEAM.md.

Product lines, changesets and merge

A product line is the other thing people mean by "branch". Windows, Windows Server, Windows Embedded — variants that live for years, share most of their ancestry, diverge on purpose and are never merged back. Forking one costs pointers, not bytes, because both lines point at the same content-addressed rows. And the question that quietly rots a product family gets a straight answer:

lac line fork base enterprise-tier    # 0 bytes copied
lac line drift                        # we fixed that — did we fix it there?
line             | path          | parent_hash  | child_hash   | parent_moved
-----------------+---------------+--------------+--------------+-------------
 enterprise-tier | core/auth.py  | 4f2a1c8e9b03 | 8d71e0a5c2f4 | 07-29

Git cannot answer that. A cherry-pick leaves no durable record that two lines now agree, so it decays into archaeology across branches nobody has opened in a year. Here it is a lookup: same path, different hash. There is no merge, so there is nothing to resolve — you are shown the fact and you decide.

Changesets, merge, and the rest of it. A lac change is the useful half of a branch: N files that land together or not at all, with review that a later edit invalidates, and a compare-and-swap so it cannot clobber someone. lac merge is a real three-way merge for the case locks cannot prevent — two federated peers, or work done offline — using git's own merge algorithm, because that algorithm was never the problem.

What git keeps. Short-lived topic branches you intend to merge back, and the plumbing around them. If your workflow is "everyone edits main freely and we sort it out at review time", git is the better tool for that part and this sits on top. See docs/VERSIONING.md for the full map of what overlaps.

./quickstart.sh                               # finds or starts a Postgres, installs everything

lac doc index . --source my-repo            # read the codebase
lac doc extract && lac web build          # map it
lac web gaps                                # what is unfinished
lac goal add "Ship multi-tenant support"    # state the objective once
lac work                                    # agents drain the board — no "keep going"

No daemon, no broker, no framework to adopt. A peer is anything that can reach the database: a shell script, an MCP client, a psql session. Your Claude agents, someone's local Llama, a GPT worker, the contractor in Lisbon, and you — same board, different vendors, no sandbox.

19 migrations · 20 commands · 23 test suites · CI on Postgres 14/16/17 · macOS, Ubuntu, Linux Mint. Runs unattended: a keeper reclaims dead agents' work, budgets stop runaway spend, and Postgres is the only moving part.


A worked example: you inherit a codebase

Six years of accretion, two people who wrote most of it gone, and a mandate to ship something on top of it. Normally you point an agent at it and hope.

1 · Let it read the whole thing.

lac doc index . --source my-repo
lac doc extract
lac web build

Not a vector blob — a structured map. Language, line count, every symbol defined, every import, which file names which. Content-addressed, so the same vendored file in three places is one document with three contributions.

2 · Ask what you actually have.

lac web gaps
files 92 · 6 languages · 13582 lines
files_no_doc_mentions   29        symbols_nothing_uses   122
files_nothing_names      2        files_no_test_mentions  86

Real output from this repository indexing itself. 29 files that no documentation mentions. 122 symbols nothing else calls. 2 files nothing in the tree names at all — entry points, or dead.

That last number is the one nobody ever has. It is not in git, not in your issue tracker, and not in your agent's context window.

3 · Ask about one thing.

lac doc symbol lac_claimable
  DEFINED IN

  short_hash  |     file      | language
--------------+---------------+----------
 6b77b9eedc6f | 002_fleet.sql | sql
 bb4ba56a5245 | 003_scale.sql | sql

Defined in two places. An agent about to "fix" one of them now knows about the other — which is exactly the class of thing it would otherwise get wrong confidently.

4 · State the objective and let it decompose.

lac goal add "Document and harden the codebase" \
  --criteria "every command has a doc mention" \
  --criteria "no orphaned files" \
  --depth 3 --budget 25.00

lac goal plan 1
                     tree                     | id | kind
----------------------------------------------+----+------
 ○ Document and harden the Lacunari codebase |  1 |
   ○ Document lac-peer                      |  2 | work
   ○ Document lac-hook                      |  3 | work
   ○ Document lac-web                       |  4 | work
   …

Nothing invented. Every subtask names a real file, and carries the evidence that produced it — lac-web (bash, 341 lines) is mentioned in no .md file — because the planner is shown the map rather than asked to imagine one.

And it expands one frontier at a time — not a 400-item plan written by something that has learned nothing yet. Five subtasks now, work starts on them immediately, and their children get planned once those five report back. Depth capped, budget checked before it spends, a wrong branch pruned before its subtree exists. Tested to 50,000 tasks in a tree, four workers draining it concurrently.

5 · Everyone works the same board.

lac work            # an agent claims and drains
lac board           # a human watches

lac work does not need to be told what to do. It asks the board, takes the top thing it is capable of, does it, records what it cost, and asks again. Run five of them and you have five agents working in parallel on one plan — this is the sentence that replaces a hundred "keep going"s.

One task, one owner, enforced by the database, so two agents cannot pick up the same file. An agent dies mid-task at 3am, its lease expires, the work returns to the board and someone else takes it. Something needs a judgement call and it escalates to a human instead of guessing.

Route work by what a worker is: requires: gpu, requires: prod-access. The agent with production credentials gets the deploy task; the one without never sees it.

6 · In the morning.

overnight   14 done · 3 blocked on you · $2.18 · 40m human
            goal #1  ████████░░  8 of 11

Costed in dollars and human hours, because your people are the expensive part. Every result content-addressed and attested, so you can check the work instead of trusting it — a second agent can re-derive it and the two are compared by hash.

7 · And the roadmap maintains itself.

Somebody writes the docs for lac-doc. On the next nightly pass:

withdrew 1 task(s) that are no longer suggested
filed 2 task(s) — already-open suggestions were skipped

The task deletes itself, because the condition that created it is gone. The board reflects the repository, not a pile of stale tickets nobody trusts.

That is the loop: the code describes itself, the description becomes the roadmap, the roadmap gets worked in parallel, and finished work quietly leaves the board. Nobody is narrating it one prompt at a time.

Same engine, any corpus

The map does not care that it is code. Point it at scanned documents and the same machinery reads EXIF, dates and place names instead of symbols and imports — and "which years does this record skip?" comes out the same way "which files has nobody documented?" does. See docs/RESEARCH.md for the archive case, and docs/CODEBASE.md for the code one.


Why this exists

Agent frameworks usually assume one process, one language, one vendor. That breaks the moment you have Claude Code in one window, Cursor in another, and a scheduled job on a server — three agents that could help each other and instead each rediscover the same context and redo the same work.

Lacunari makes the coordination layer something all of them can already talk to. The integration surface is "can you run a command, or speak MCP" rather than "can you import this SDK", which is why a bash cron job and Claude Desktop can be peers on the same queue.

It solves three specific problems:

  1. Work outlives the agent doing it. A task is a database row, not something held in a session's context. If a session crashes, its claim goes stale, the keeper releases it, and someone else picks it up. Nothing is silently lost.
  2. Two agents don't do the same job. Claiming is a single atomic UPDATE. The loser is told to pick something else.
  3. Stuck work reaches a human. Blocked and high-priority-unclaimed tasks escalate on a 30m/2h/6h backoff to whatever channel you configure. This is the part most setups get wrong, and the reason the keeper looks the way it does — see the keeper's header comment.
  4. Nobody has to write the roadmap by hand. The board is only useful if something fills it. Indexing what you already hold produces the gaps, and the gaps are the work — including in a codebase, where they are undocumented modules and symbols nothing calls. When a gap gets filled, the task withdraws itself.
  5. Enormous goals stop being unusable. An objective is expanded one frontier at a time rather than planned up front, because the decomposition at depth 4 depends on what was learned at depth 2. Work starts before planning ends, and a runaway costs one expansion instead of a budget.

What it is not

Being clear about this up front, because the category invites assumptions:

  • Not an agent runtime. It doesn't run models or manage context. You bring the agents; this coordinates them.
  • Not a security boundary — until you turn one on. By default cperm and crule are cooperative gates: they constrain agents that ask, not agents that don't. For a team, team mode makes the database enforce it — identity comes from the connected Postgres role and cannot be spoofed, and lane permissions are row-level security rather than convention.
  • Not a workflow engine. Tasks can depend on each other and nest into departments, but there is no fan-out/fan-in, no retries, no conditional branching. If your problem is a graph, use a graph engine.

The pieces

Command What it does
csend cread cwho cwatch the bus — post, read, see who's active, live tail
ctask the durable queue — add, claim, beat, done, block
cagent cperm identity, and the cooperative permission gate
crule policy — allow / require-approval / deny, by exit code
crole role briefs (system prompt + lane + definition of done)
cbuild decompose a goal into role-matched queued tasks
cask cresolve cstatus put a question to a human; report what you're doing
lac work a worker — claims, runs, heartbeats, survives being killed
cboard the whole fleet on one screen, live off LISTEN/NOTIFY
lac spend what it cost, by lane/agent/task/model — and hard budgets
lac reconcile keeps reality matching what you declared — maintenance, not cron
lac doc the library — catalogue, search, dedup, inventory
lac peer federate with another group's catalogue
lac attest / lac verify results others can re-derive, not just believe
lac ingest pull work in from GitHub, Jira, Linear, anything with JSON
lac hook push task events out to Slack, CI, anything with a URL
lac digest what happened while you were away
lac replay <id> one task's whole life, from the record
lac one entry point for all of the above, plus lac doctor
lac-admin add, list and revoke members (team mode)
cpod sub-teams, once one queue gets crowded
tasks/lac-keeper.sh releases stale claims, escalates stuck work — run this on cron
tasks/lac-drain.sh runs queued tasks through your executor, unattended

Everything gates by exit code, so it composes with anything:

crule check deploy-fix && ./deploy.sh          # 0 = allow
crule check money-transfer || cask "approve?"  # 2 = ask a human
cperm "$LAC_NAME" deploy || exit 1           # default-deny

Running work unattended

Start a worker on any machine. It claims what it is permitted and able to do, runs it through your executor, heartbeats while working, and puts the claim back if it dies:

export LAC_EXECUTOR=$PWD/executors/claude-code   # or executors/shell, or your own
lac work --lanes backend,review --concurrency 3

Run one on your laptop, one on a server, one on a Pi — they all pull from the same queue. An executor is any program that takes a task on stdin and exits 0 (done), 2 (refused), or anything else (blocked with the reason). There is no default executor, no default model and no default vendor: nothing spends your money until you wire it up. See executors/.

Work survives the worker. Kill one mid-task and the claim goes stale, the keeper releases it, and another worker finishes the job:

lac work --lanes auto &      # claims #1
kill -9 %1                     # hard kill, mid-task
tasks/lac-keeper.sh          # releases the orphaned claim
lac work --lanes auto --once # a different worker completes it

Chains, capabilities and budgets

ctask dep 3 needs 2                    # 3 cannot start until 2 is done
ctask ready                            # what you could actually claim now
cagent register box-pi --owner me --caps arm64,camera
lac spend budget lane:research 5.00  # a hard daily cap; work stops, never overspends
lac spend --by task                  # where the money went

cbuild chains its recipe steps automatically, so a reviewer cannot start before there is anything to review. See docs/COSTS.md.

Running it with other people

The default install trusts everyone who holds the connection string, and takes your word for who you are. That is right for one person and wrong for a team.

Team mode gives every person and agent their own Postgres role, and moves identity and permissions into the database:

psql "$LAC_ADMIN_DSN" -f schema/team-mode.sql
bin/lac-admin adduser alice --perms research,docs --role researcher
bin/lac-admin revoke alice          # cuts live sessions, not just new ones
bin/lac-admin audit alice           # append-only; no member can rewrite it

After that, LAC_NAME=someone-else does nothing — the server stamps the sender from the connected role — and an agent scoped to research cannot claim a deploy task even from raw psql. Verify it yourself rather than trusting the claim:

tests/run-all.sh team-mode      # the boundary, asserted from a hostile client

Setup, TLS, and the honest list of what it still doesn't protect: docs/TEAM.md.

MCP

Any MCP client can join a fleet without installing the CLIs — it talks to the database directly, so the client can be on a laptop and the fleet on a server. See mcp/README.md.

{"mcpServers": {"lacunari": {
  "command": "uvx",
  "args": ["--from", "/path/to/lacunari/mcp", "lacunari-mcp"],
  "env": {"LAC_DSN": "postgres://…", "LAC_NAME": "desktop-claude"}}}}

Configuration

Variable Meaning
LAC_DSN the database — the only required setting
LAC_NAME this node's stable identity; required, never invented for you
LAC_ESCALATE_CMD how the keeper reaches a human (unset = bus only, which is the weak path)
LAC_EXECUTOR the program the drainer runs tasks through
LAC_DRAIN_LANE which lane the drainer works (default auto)
LAC_LEASE_MIN how long before a silent claim is released (default 20)
LAC_COORDINATOR who gets named in the keeper's nag

CLAUDE_NAME is accepted as an alias for LAC_NAME, for existing deployments.

Building on it

There is no bundled web UI, deliberately — a board is the part every team wants differently. What ships instead is a surface to build one against:

ctask list --json          # --json on every read command
cboard --json              # lanes, tasks, presence and inbox in one object
psql "$LAC_DSN" -f schema/views.sql   # stable views: the integration contract
LISTEN lac_update;       # push, not poll

docs/INTEGRATING.md has the details, including the rules for writing your own peer without these CLIs at all.

Drops into what you already run

Most teams adopting this already have GitHub Issues, Jira, PagerDuty and a Slack channel. A coordination layer that ignores those is a second silo, and the second silo loses. So work flows both ways on day one:

# in — idempotent, so it is safe on a webhook AND on a timer
gh issue list --repo acme/api --json number,title,body,url,state \
  | lac ingest --source github --map gh --lane backend

# out — an outbox with retry, so Slack being down delays but never loses
lac hook add slack "$SLACK_URL" --template slack --lanes ops,deploy
lac hook add ci "https://ci.internal/lacunari" --events task.done,task.blocked --secret "$KEY"

Ingest is keyed on (source, external_id): re-syncing 500 issues creates zero duplicates. Closing an issue upstream closes the task here — but an upstream sync can never reopen work someone here already finished. Built-in mappings for github, jira, linear, plus generic for everything else.

See connectors/README.md.

The library, the web, and goals

The queue tracks what you are doing. The library tracks what you have, and for a group pooling material that is the half that turns into a mess first.

lac doc add scans/*.pdf --source alice --subject "Operation Market Garden"
lac doc index . --source my-repo       # a repo is just a corpus that compiles
lac doc extract                        # facets: EXIF, symbols, imports, PDF metadata
lac doc symbol lac_claimable         # defined where, imported by whom
lac doc near 51.9797 5.9110 5          # photographs within 5 km of the bridge

Documents are keyed by content hash, so three people uploading the same scan under three filenames produce one document with three contributions — nobody's attribution is lost, and independent copies count as corroboration. Where two sources disagree, both are kept and the contradiction is recorded rather than quietly resolved. LIBRARY.md · INDEXING.md · CODEBASE.md

Indexing gives you facts; the web gives you relationships and the holes in them.

lac web build                          # subjects, mentions, edges
lac web path "Jacques Goudstikker" "Altaussee"
lac web gaps
lac web suggest --dispatch             # → the task queue

Similar names are proposed for merging, never merged silently. An archive that quietly decides two spellings are one person has destroyed evidence. RESEARCH.md

Goals run the other direction — top-down, an objective decomposed into a tree agents can claim. Not planned up front: the decomposition at depth 4 depends on what was learned at depth 2, so leaves are expanded when they are reached, with their siblings' findings in hand. Work starts before planning ends, and a wrong branch is pruned before its subtree exists.

lac goal add "Establish what happened to Portrait of a Young Man" \
  --criteria "A dated chain of custody from 1939 to 1945" --depth 3 --budget 25.00
lac goal plan 1        # expand ONE level, grounded in what you actually hold

Depth is capped, budget is checked before expanding, malformed planner output is dropped rather than filed, and every expansion records what the planner was shown and what it returned — so "why is Lacunari doing this?" has an answer. planners/offline needs no model at all. GOALS.md · planners/README.md

And two groups can connect without either surrendering their catalogue:

lac peer add kew https://kew.example/catalogue.json
lac peer pull kew
#   2 they hold that we do not · 1 overlap · 1 titled differently

Search their catalogue without holding a byte of it, then fetch only what is worth having, hash-verified on arrival. A peer's catalogue is evidence, not authority: pulling never overwrites your metadata. FEDERATION.md

Results anyone can check

A result is prose — it says an agent claims something happened. For research, and for anywhere a stranger has to build on your answer, that is not enough:

lac attest 41 --output scores.csv --claim "ranking of the 3 windows" \
                --method "./score.py" --inputs candidates.csv --exact

lac verify --all        # on someone else's machine
#   ✓ #1  alice  reproduced  ranking of the 3 windows

A hash proves the bytes are unchanged; it does not prove they are right. What counts is how many independent peers re-derived them — so --exact means "deterministic, go ahead and re-run it", and the default recorded means "pinned and unalterable, but I am not claiming a re-run reproduces this." Marking a sampled model output --exact would fail verification for an honest reason, so the modest default is the right one.

Disagreements are recorded, not hidden. Attestations cannot be edited or deleted. See docs/VERIFIABLE.md.

Maintaining, not just building

A build queue terminates. Maintenance does not — the certificate expires again, the docs drift again. Cron is the wrong shape for it: it creates work whether or not anything is wrong, and the queue fills with no-ops.

Declare the state you want instead. An observer reports what is. A task is raised only for the gap — and withdraws itself when the gap closes:

lac reconcile add cert-expiry \
  --component "api.example.com" \
  --intent    "the TLS certificate has more than 30 days left" \
  --observer  "$PWD/observers/tls-expiry api.example.com 30" \
  --every 3600

However often it runs, drift produces exactly one task; repair closes it with no one tidying up. See docs/MAINTAINING.md.

What it really costs

Model spend is the cheap half. Human time is the other half, and a budget that counts only API calls tells a project manager nothing:

lac spend time 42 90 150       # 90 minutes at $150/hour
lac spend --by true            # model + labour, the real number
lac spend budget tree:7 6h     # cap a department's HUMAN time, not just $
 lane   | tasks | human_min | model_cost | labour_cost | total
--------+-------+-----------+------------+-------------+--------
 design |     1 |     135.0 |       0.18 |      337.50 | 337.68

Either ceiling stops work — whichever runs out first.

The night shift

The point of all of it:

lac work --lanes backend,docs --concurrency 3 &   # on each machine
lac spend budget global 10.00                     # a ceiling it cannot cross
# go to bed
lac digest                                        # in the morning
LACUNARI DIGEST — last 12 hours
  2 shipped · 0 still open · 1 blocked · 1 waiting on you · $0.04 spent

SHIPPED
  #2  [auto]  Refresh the search index  (night)
  #1  [auto]  Rotate the log archives  (night)

NEEDS YOU  (nothing moves on these until a human acts)
  blocked-task  #3    0m    Renew the TLS certificate
  → executor error rc=4: cannot reach the DNS provider

Docs

Requirements

Postgres 12+, psql, bash 4+. The MCP server needs Python 3.10+.

License

Copyright © 2026 Evan King. Licensed under the GNU Affero General Public License v3.0 or later — see LICENSE.

In short: use it, run it, modify it, self-host it, freely. If you distribute it or offer a modified version to others over a network, you have to release your source under the same terms.

Commercial licensing. If that does not work for you — you want to build Lacunari into a proprietary product, or offer it as a hosted service without publishing your changes — a commercial license is available. Contact hello@lacunari.com. Every contribution is covered by a CLA, so the terms can actually be granted.

Popular repositories Loading

  1. lacunari lacunari Public

    Coordination infrastructure for people and machines. Indexes a codebase or archive, reports what is missing from it, and lets any number of humans and AI agents work the resulting queue in parallel…

    Shell 1