Privacy posture, the LLM passes, and budgets
Three decisions to make once, deliberately. Two of them cost money; all three affect trust.
1. Privacy posture
Section titled “1. Privacy posture”The full model is in Privacy & security model. The
decision you own is privacy_mode.
| Mode | You see | Good for |
|---|---|---|
k_anon (default) |
De-identified aggregates, withheld below 4 distinct contributors | Any org where individual visibility hasn’t been explicitly agreed |
open |
Named, per-individual results | A small team that has agreed to it — often one where the founder is also a contributor |
# Per-org (preferred, especially on a shared server)curl -X PUT https://manthana.acme.com/v1/admin/orgs/acme/privacy \ -H "X-Admin-Token: $ADMIN" -H 'Content-Type: application/json' \ -d '{"privacy_mode":"open"}'
# Server-wide defaultMANTHANA_SERVER_PRIVACY_MODE=openTwo things do not change in open mode: personal-mode sessions still never sync,
and every named query is still written to the audit trail flagged as individual.
open widens what you may ask; it does not widen what was collected.
Have the conversation before you flip it. The switch is one line of config; the trust it spends is not recoverable. Tell your team which mode you’re in.
The k-anonymity floor
Section titled “The k-anonymity floor”MANTHANA_SERVER_K_ANON defaults to 4. Below it, cross-engineer views are
withheld rather than shown thinly — org rollups, topic clusters, skill mining,
and most weekly-digest sections.
The practical consequence: with three engineers the digest will be mostly empty, and that is working correctly. Onboard the team. The only legitimate reason to lower the floor is a genuinely single-person install, where there is nobody to de-identify from — see Solo use.
2. The server-side LLM passes
Section titled “2. The server-side LLM passes”This is the fact most new operators miss: the server does nothing LLM-shaped until you turn it on. All three passes default to off. Out of the box the server faithfully stores digests, serves the console, and writes no wiki articles at all.
That default is deliberate. Each pass is a background loop that spends real money and sends text to a model provider. An operator should choose that, not discover it on an invoice.
| Pass | Flag | Model | Does |
|---|---|---|---|
| Enrichment | MANTHANA_SERVER_ENABLE_ENRICHMENT=1 |
claude-haiku-4-5 |
Fills the qualitative fields on the deterministic digests agents send. Everything downstream depends on it. |
| Consolidation | MANTHANA_SERVER_ENABLE_CONSOLIDATION=1 |
claude-haiku-4-5 |
Turns enriched digests into typed notes: decisions, conventions, gotchas, benchmarks. Requires enrichment — it only reads digests enrichment has filled in. |
| Project overviews | MANTHANA_SERVER_ENABLE_PROJECT_OVERVIEW=1 |
narrative model | Writes the living “what this project is” article per project. |
Plus a real model. Four ways:
# 1. The Anthropic API — the normal choice for a deployed serverMANTHANA_SERVER_LLM=anthropicANTHROPIC_API_KEY=sk-ant-…
# 2. Bring your own: a Claude CLI logged in as the user running the server,# so you spend the subscription you already have instead of a second key.MANTHANA_SERVER_LLM=claude_cliMANTHANA_SERVER_CLAUDE_CLI=claude # only if the binary isn't called `claude`
# 3. OpenAI, or OpenRouter's one key in front of hundreds of modelsMANTHANA_SERVER_LLM=openrouterOPENROUTER_API_KEY=sk-or-…MANTHANA_SERVER_LLM_MODEL=anthropic/claude-3.5-sonnetMANTHANA_SERVER_ENRICH_MODEL=openai/gpt-4o-miniMANTHANA_SERVER_CONSOLIDATE_MODEL=openai/gpt-4o-mini
# 4. Your own model, on your own hardware — see belowMANTHANA_SERVER_LLM=openaiMANTHANA_SERVER_LLM_BASE_URL=http://vllm.internal:8000/v1MANTHANA_SERVER_LLM_API_KEY=whatever-your-server-acceptsChanging provider means changing all three model ids. They default to
Anthropic ids (claude-sonnet-4-6, claude-haiku-4-5), so pointing the server at
openai or openrouter and leaving them alone makes every call fail — and
the failure is quiet, because a failed pass degrades rather than raising. Note the
vendor prefix on OpenRouter ids (openai/gpt-4o-mini); OpenAI’s own ids have none.
manthana-server doctor checks this for you.
claude_cli works on a laptop and not in a container. It needs the binary
and a logged-in $HOME, so it’s right for a server you run as yourself and
wrong for the published images. If the binary isn’t reachable the server logs
loudly and falls back to the mock — it doesn’t crash, and the wiki stays honest
but empty. Its cost is metered from what the CLI actually reports rather than
estimated from a price table.
Without any of them, narratives come from a deterministic mock that returns “insufficient data” rather than inventing something.
Keeping session text on your own infrastructure
Section titled “Keeping session text on your own infrastructure”The openai provider is only conventionally OpenAI. Point
MANTHANA_SERVER_LLM_BASE_URL at any server that speaks the same chat-completions
API — vLLM, Ollama, LM Studio — and the entire pipeline runs against a model you
host:
MANTHANA_SERVER_LLM=openaiMANTHANA_SERVER_LLM_BASE_URL=http://vllm.internal:8000/v1MANTHANA_SERVER_LLM_API_KEY=whatever-your-server-accepts # often any non-empty stringMANTHANA_SERVER_LLM_MODEL=Qwen/Qwen3-32B-Instruct # your server's id, not Anthropic'sMANTHANA_SERVER_ENRICH_MODEL=Qwen/Qwen3-32B-InstructMANTHANA_SERVER_CONSOLIDATE_MODEL=Qwen/Qwen3-32B-InstructThis is the configuration to reach for when the answer to “may a third party read our sessions?” is no. Enrichment, consolidation and project overviews send session text to whatever the model provider is; with a self-hosted endpoint that provider is you, and no session text leaves infrastructure you control. It costs nothing extra to support because the wire format is identical — the same provider class serves all three cases.
The trade is quality: enrichment and consolidation are structured summarization that must return parseable JSON, and a small local model is worse at it. Turn enrichment on alone first and read a few articles before enabling the rest.
Note also that spend figures become meaningless here — an unrecognised model id is
costed at Sonnet-class list rates, which nobody is charging you. Leave
MANTHANA_SERVER_LLM_MONTHLY_CAP_USD=0 and read the token counts instead.
A working wiki configuration:
MANTHANA_SERVER_LLM=anthropicANTHROPIC_API_KEY=sk-ant-…MANTHANA_SERVER_ENABLE_ENRICHMENT=1MANTHANA_SERVER_ENABLE_CONSOLIDATION=1MANTHANA_SERVER_ENABLE_PROJECT_OVERVIEW=1MANTHANA_SERVER_LLM_MONTHLY_CAP_USD=100Turn them on in that order and give enrichment a pass or two to drain before enabling consolidation — consolidation has nothing to read until enrichment has run.
Every bound (batch sizes, intervals, retry limits) is listed in
Environment variables.
The defaults are sane; the ones worth knowing are ENRICH_INTERVAL (300s) and
ENRICH_BATCH_PER_ORG (25), which together mean a large backlog drains over
hours, not minutes, and cannot starve other tenants.
Cost, roughly
Section titled “Cost, roughly”Enrichment and consolidation run on Haiku and are bulk structured summarization,
not reasoning — on the order of a cent or two per session. On openai the same
work is cheaper still at gpt-4o-mini rates. Project overviews are
bounded by a hash of each project’s contributing sessions: they regenerate when
the work changes, not on a timer, and never re-describe a project a human has
edited. Watch real numbers rather than trusting an estimate:
manthana-server usage acme --server-url https://manthana.acme.com3. Budgets
Section titled “3. Budgets”# Server-wide default for every org (0 = unlimited)MANTHANA_SERVER_LLM_MONTHLY_CAP_USD=100
# …or per org, at any timemanthana-server set-quota acme 100 --server-url https://manthana.acme.comThe shipped default is 0 — unlimited. That’s right for a self-hoster paying
their own model bill: being throttled by a number someone else chose is worse
than the bill. Usage is still recorded either way.
If you run a hosted, multi-tenant server you are spending your own money on
other people’s orgs, so set a real cap. manthana-server onboard-org already
provisions each new customer org with an explicit $100/month override.
$100 rather than something tighter because the failure mode of a low cap is
invisible and awful: enrichment stops, every session stays pending, and the
wiki quietly fills with unsummarised work that looks like a bug rather than a
bill. The cap exists to stop a runaway, not to ration normal use.
How exact the numbers are
Section titled “How exact the numbers are”Spend is estimated from token counts at API list price, from a deliberately
small table (Claude tiers, gpt-4o, gpt-4.1, o4-mini, gpt-5, and the -mini
variants). An id the table doesn’t recognise is costed at Sonnet-class rates, which
may be wildly wrong in either direction.
Two providers do better, because they tell us what the call actually cost and a measured cost always beats an estimated one:
openrouter— the server asks for per-call cost on every request, so a router deployment gets exact spend across however many models you route to. That is the reason to prefer it if precise budget tracking matters.claude_cli— reports what the call cost on your own subscription.
Watching spend
Section titled “Watching spend”manthana-server usage acme --server-url https://manthana.acme.com# org=acme cap=$100.00/mo (org override)# 2026-07 $12.4180 (843 calls, 4,102,933 in / 210,447 out tokens)For “which pass is eating the budget”, the HTTP endpoint breaks spend down by
purpose (enrich, consolidate, and the narrative passes):
curl -H "X-Admin-Token: $ADMIN" \ "https://manthana.acme.com/v1/admin/usage?org_id=acme"That response also returns spent_usd for the current month and a plain
quota_blocked boolean, read from the same row that actually gates the passes —
so it can never disagree with the thing doing the blocking. Check it first when
the wiki stalls: an exhausted cap raises no error anywhere a human looks. It just
stops enrichment, and every session stays pending, which reads as a bug rather
than a bill.
→ Reading the wiki & the founder digest → Operating the server