monban(門番)
monban lets a server with no open inbound port be used from outside — by people, machines and LLMs — through a gate that checks keys, holds deliveries until the house fetches them, listens for the house's pulse, and keeps a ledger. It never enters the house.
外から入る口を開けないサーバ(ハウス)を、外の人・機械・LLM から使えるようにする門番。キーで人を確かめ、リクエストを預かってハウスが取りに来るのを待ち、ハウスのパルスを聞き、記録を付ける。ハウスの中には入らない。
Status (2026-10-19): 1.0.0. The public contract, the compatibility table, the remote MCP entry for AIs without a CLI, recovery after a lost conversation, and failure/update/relocation/retirement drills are accepted with independent rebuilds and checks. The reference Edge runs at https://monban.me; a self-hosted Edge uses the same code (edge/ in the source). Adoption by outside users is being validated separately and is not a 1.0 condition. The source repository is currently private; its design documents (mostly Japanese) are referred to below by path.
状態(2026-10-19): 1.0.0。公開契約・互換表・CLI の無い AI からの遠隔 MCP の入口・会話が切れた後の回収・故障/更新/移転/撤去の訓練を、作り直しと独立の確かめで受け入れた。外部の利用者による導入は 1.0 と別に確かめている。source の repo は今は private で、設計の文書(主に日本語)は下でパスだけ示す。
Let people, scripts and external agents run allowed operations of an existing CLI on a server with no open inbound port. Refusals and safe continuation are part of the contract. monban provides the resident House and trusted Edge; app authors keep business rules. It contains no LLM. 1.0 means the users of the apps behind the gate can authenticate from their own AI (a local CLI, or an AI with no CLI through the remote MCP entry), use the allowed operations, and verify what happened afterwards. App authors write v2 requirements (min_cli_major_minor: "1.0"); v1 pins major.minor exactly.
日本語: 既存CLIの許した操作を人・スクリプト・外部agentが同じ道で使い、断りと安全な続きまで契約にする。1.0 は「載せたアプリの利用者が、その人の AI(手元の CLI か、CLI の無い AI からは遠隔 MCP)から認証して使い、後から確かめられる」こと。app 作者は v2 の requirements(min_cli_major_minor: "1.0")で書く(v1 は major.minor の完全一致)。
Three parts: Client (monban.client, CLI monban), Edge (edge/, a Cloudflare Worker; see edge/README.md), House (monban.house, a daemon library that pulls from the Edge and calls your handlers). The Python package needs only the standard library (and OpenSSL 3 on the house, for its signing key).
How it looks to the person who runs a house / ハウスを持つ人から見た形
Start with the self-host first-read runbook (docs/運用_monban_導入と移転撤去_2026-10-03.md, section "self-host first read", in the source): the fixed release, your own Edge origin, separate private admin/House/Client paths, invitation through a pipe or hidden terminal input, then one foreground read. A hosted user gets the invitation from that Edge's operator. Do not put invitation codes in arguments or chat; the runbook keeps every House command on the same chosen private root.
日本語: 初回は上の一本道を使う。自分のEdge・private配置・安全な招待入力・foreground読取りを先に確認し、作者のdomainや既定rootへ黙って接続しない。
After setup, a custom app supplies a table of "operation name → function" and its own House configuration for the selected private root/Edge. This app.py example shows the handler interface, not a second installation recipe:
from monban.house import House
def my_status(ctx, body):
return {"ok": True, "account": ctx.account}
house = House(handlers={"echo": lambda ctx, body: {"echo": body}, "status": my_status})
if __name__ == "__main__": # `python app.py` runs it; `monban house run --handlers app.py` imports it
raise SystemExit(house.run())
Remote-MCP clients (AI connectors) of an owner are listed and revoked from the house's machine with monban house owner devices <owner> and monban house owner revoke-device <owner> <device-id>: signed by the house, no owner secret typed; revoking kills the client's tokens at the Edge now and drops that account's key on the house. 日本語: AI クライアントの一覧・失効はハウスの機械から、オーナー secret を入れずにできる。
monban house run --handlers takes path/app.py[:attr] or module[:attr]. Without :attr, it selects house, then handlers, and expects a House or a plain handler dict. A zero-argument factory must be selected explicitly, for example app.py:make_house or monban.reference:house; it must return either of those types. Run it under systemd with Restart=on-failure: when the code on disk changes (a pip upgrade, a new app.py) the house exits with 75 and comes back on the new code. Code-change restart is not a result-ACK drain; for live work use monban house quiesce and the runbook's update procedure.
日本語: factoryは :属性名 を明示する。省略時のhouse/handlersはHouseまたはdictを置く。コード変更による再起動だけでは結果ACKまでの収束を保証しない。
日本語: 初回のowner画面・本人loginもrunbookの自分のoriginへ向ける。上のPython例は業務handlerの接点で、private配置・登録手順はrunbookを使う。
Handlers / handler の書き方
handler(ctx, body) -> dict.bodyis the caller's JSON object (it includesaccount).ctx.owner,ctx.account,ctx.operation,ctx.request_id,ctx.credential(first 12 hex of the key's hash),ctx.scope.- Refuse with
raise monban.Refused("code", reason="…", next_at="ISO"). The code reaches the caller as is (never rounded into a vaguer one). Any other exception becomesrequest_failed. ctx.check("publish")asks the safety catch (limitsandeventsgiven toHouse);ctx.log(event, diff)writes to the ledger.- Re-delivery of the same request ID does not run its handler again while its execution mark is retained:
state/<account>/requests/<id>.json(0600, no body, retained for 24 h). A request that was running when the house died answersoutcome_unknown. The Client saves an ID before sending and retries with that ID; a newcallis another delivery. This is not a business exactly-once guarantee.
The House checks owner stop and current credentials before every handler, including command-table handlers. Stop takes effect when the House applies the pulled action; it does not cancel work already running. 標準 commands も実行前に停止と資格を検査する。停止はハウスが action を反映した時点から未着手操作に効き、実行中の業務取消はアプリ側の責任となる。
handler は (ctx, body) を受けて JSON の object を返すか、monban.Refused("エラーコード", reason=…, next_at=…) で断る。エラーコードはそのままリクエストした人に届く。
What else House takes
| argument | what |
|---|---|
root |
the house root (default MONBAN_HOUSE_ROOT or ~/.config/monban/house) |
credentials |
path of credentials.json (default in root), or an object with the same methods |
signer |
path of the signing key or a Signer (default root/signer.key) |
edge_url |
the Edge origin (default MONBAN_EDGE_URL, house.json, then https://monban.me) |
summary(owner, accounts) |
what the owner's page shows (JSON) |
actions(owner, action) |
owner actions other than the built-in stop, resume, revoke, rotate |
events(account), limits |
the safety catch: [{"kind", "at"}] and {"daily", "min_interval_seconds", "burst"} |
on_pulse(row) |
pulses sent by clients (monban pulse) |
protocol |
monban-v1 (default) or thth-approval-v1 (THTH's Edge) |
interval, busy_interval, idle_interval |
normal / busy / maximum quiet pull cadence (seconds; default 10 / 2 / 60) |
After empty syncs, the House waits 10, 20, 40, then at most 60 seconds. Waiting handlers, unacknowledged
results, owner actions and pulses reset the cadence; work syncs every 2 seconds. A new request, stop,
revoke or login rotation arriving during a quiet period has a scheduled quiet wait of at most 60 seconds
(30 seconds on average for uniformly timed arrivals), compared with 10 / 5 seconds at a fixed 10-second
cadence. The run loop adds up to one second of tick delay; earlier owners in the round, network calls,
and handler execution can add further time. This is a scheduling bound, not an end-to-end completion
guarantee. A slow handler is not guaranteed to finish within the Client's 90-second wait; use the delivery
receipt to retrieve the result afterward. Once pulled, stop and credential checks still apply before every handler, and results are sent
as soon as the House drains a finished handler. Set idle_interval=interval for a fixed cadence, or set
an explicit maximum. The default maximum is six times a custom interval.
The Edge keeps one durable nonce row and one combined report row per sync to preserve replay rejection after restart and exact report freshness. With three quiet owners and no published capabilities, 60-second pulls use 8,640 local SQLite KV row writes per steady day (8,652 on the first day as the cadence lengthens). This excludes alarms, requests, results, keys, actions, pulses and login activity; published capabilities retain their full freshness and add one Account row update per exchange. These are local runtime measurements and a cadence calculation, not production Cloudflare billing totals.
空の同期が続くと 10 → 20 → 40 → 60 秒へ延ばす。静かな間に届いた呼出し・停止・失効・ログインの
鍵切替の予定上の待機は最大 60 秒(均等到着なら平均 30 秒)。実際にはrun loopのtickで最大1秒、
同じroundで先に同期するowner・通信・handler実行の時間が加わるため、受信や完了までの時間の保証ではない。
遅いhandlerがClientの90秒待機内に完了する保証もなく、配送receiptを用いた後続の結果回収と区別する。
実行中の仕事や保存 ACK のない結果があれば 2 秒で同期を続ける。idle_interval=interval で従来の固定間隔にもできる。
house.pulse(name, fail=False, note=None) records "this job ran" (or failed) and sends it with the next sync.
credentials.json lists owners and key hashes (never keys):
{"schema_version": 1,
"owners": {"alice": ["alice-blog"]},
"credentials": [{"sha256": "<hex64>", "owner": "alice", "accounts": ["alice-blog"],
"expires_at": "2027-09-27T00:00:00+00:00", "revoked": false}]}
How it looks to the people who use a house / ハウスにリクエストする人から見た形
pip install monban # Python 3.12+; no dependencies
monban login --house my-house # the owner approves in the browser; the key is kept here (0600)
monban call echo --json '{"text": "hi"}' # the house's answer, as JSON
monban pulse nightly-backup --note "3 min" # "this ran" (add --fail when it did not)
monban status # logged in? which Edge? (never the key)
monban requests # private delivery metadata (no bodies or keys)
monban result <request-id> --wait # recover a result after a lost response
monban retry <request-id> # replay the saved input with the same ID
monban forget <request-id> # erase local continuation; does not cancel work
- The Edge is
https://monban.meunless--url/MONBAN_EDGE_URLsays otherwise (self-hosted Edges use the same code). - The key is never printed and never taken as an argument (
--stdinreads an issued key from one line). It lives in~/.config/monban/remote.json(0600;MONBAN_REMOTE_CONFIGoverrides). - Refusals are one code word plus the next step, in English and Japanese, with exit code ≠ 0.
--jsonprints only JSON (forcall,--jsonis the request body and the output is always JSON;house inspect/quiesce/unquiesce/recover-running/stop/retirealways print JSON). - LLMs:
monban-mcpis a thin MCP server (stdio) with eight tools — call, pulse, status, capabilities, check, requests, result and retry — that only run the CLI. There is no login tool: a person logs a machine in once, so keys never pass through a model's context. - AIs that cannot run a local CLI (claude.ai connectors, the Claude phone app): the Edge offers
/h/<house>/mcp(Streamable HTTP) behind OAuth 2.1 (/h/<house>/oauth/*, PKCE, CIMD or DCR). The owner allows one dedicated account per connector on the same consent page asmonban login; since 2026-10-17 no account is typed there (the Edge picks the AI-client account, never a CLI's, and offers a choice only when it cannot tell); the Edge keeps that account's key only as ciphertext opened by the client's token; tools are the app's published operations plusmonban_call/monban_requests/monban_result; refusals stay codeword + reason + next as tool results. Accepted with claude.ai's custom connector (design:docs/設計_monban_遠隔MCP_認証つき入口_2026-10-04.mdin the source).
クライアント(とその LLM)は monban login --house <ハウスの名前>(ブラウザでハウスのオーナーが許可)→ monban call <operation> --json '{…}'。LLM は薄い MCP(monban-mcp・CLI を呼ぶだけ)で使う。キーは画面にも引数にも出さない。
The protocol, briefly / 約束
- House → Edge, always pulled:
POST <edge>/relay/v/house/<house>/activity/<owner>/sync, RSA-PSS signed (monban-v1, headersX-Monban-Time|Nonce|Signature). Up:{summary, accounts[names], completed, keys[{sha256, account, expires_at}], results[…], pulses[…]}. Down:{requests[…], actions[…], pending, pulses[…]}(the owner-secret and devices management routes live beside it under/relay/v/house/<house>/owner|devices/…; all internal). The owner's secret:POST /relay/v/house//owner//set{salt, verifier, iterations}` (signed). - Client → Edge (all under
<edge>/h/<house>):POST /api/v1/delivery/<operation>(Bearer <key>,{request_id, body, contract?}) → 200 the house's answer, or 202 thenGET /api/v1/result/<id>(the result must reach the Edge within 180 s of the id's time; a stored result is kept 60 min).GET /api/v1/capabilities/<account>returns the published operations. The oldPOST /api/v1/<operation>remains for older clients. New clients refuse an Edge without delivery continuation, with no fallback.POST /api/v1/pulse; browser loginPOST /api/v1/login/start+GET /api/v1/login/poll/<code>+POST /api/v1/login/ack/<code>.delivery,result,pulse,loginandcapabilitiesare reserved. - Sync replies include
acknowledged[{request_id, account}]only for durably saved results, plusrejectedexpiry/conflict details. The House retains an unacknowledged result; an HTTP 200 alone never erases it. - Client continuations live beside the config in
<config>.requests(0700 directory, 0600 files), separate from operational logs. Input is kept privately only for the same delivery retry; success, definitive refusal, or expiry clears it.requestsprunes expired inputs (itsstate: delivery_expiredmeans the local input is gone after 120 s; the Edge may still hold the result for 60 min), andforgetremoves a chosen record. Runrequestsor remove the local directory when retiring a machine. Login poll secrets survive response loss until ACK or locallogout;monban login --canceldiscards an unreceived login without touching the key. - The Edge keeps key hashes and temporarily stores request bodies (120 s; the row 180 s) and results (60 min), processing them in plaintext. It is a trusted component; there is no end-to-end encryption. TTL is not a guarantee against recovery from retained storage history; see "retention" (§10.1) in
docs/設計_monban_全体設計_2026-10-02.mdin the source. The house rechecks credentials before execution.
Tests
python -m pytest tests/ -q -n auto -p no:cacheprovider
Edge: cd edge && npm ci && npm test (the Edge in Miniflare, and the real House and Client against it over HTTP).
The Python tests run a fake Edge (tests/fake_edge.py, http.server on 127.0.0.1) that verifies the house's signatures with OpenSSL, and drive real round trips between House and Client.
App contracts and packaged Skills / アプリ正本とSkill
The app owns its manifest and business recovery rules. See the reference manifest (monban/assets/reference-manifest.json, in the package) and app (monban/reference.py). Managed operations require their version/digest; unmanifested handlers need an explicit --legacy-operation when sharing a managed House. Contract definitions, command parameters/stdin and required fields are checked at startup and execution. Since the 0.4 source line an input or output field may be declared as an opaque object ({"type":"object","additionalProperties":true,"maxBytes":N}): monban checks object-ness, canonical size and depth, and the app checks the content; a 0.3.x House refuses such a manifest.
monban manifest check app-manifest.json --commands commands.json
monban house run --commands commands.json --manifest app-manifest.json
monban capabilities --account alice-blog
monban skills export --dest ./shared-skills # a new directory; no global installation
monban skills verify ./shared-skills
monban check --requirements ./shared-skills/monban-reference/requirements.json
monban call demo_read --requirements ./shared-skills/monban-reference/requirements.json --json '{"business_id":"trial-001"}'
monban house inspect --root <private-root> # counts/bytes, never bodies or secrets
monban house retire --root <old-root> # permanent registration retirement; no storage purge
The packaged reference House starts with MONBAN_HOUSE_ROOT=<private-root> monban house run --handlers monban.reference:house. Skills guide an external LLM and never grant permission. A chat environment unable to execute the local CLI or its MCP uses the remote MCP entry https://<edge>/h/<house>/mcp (OAuth 2.1 with PKCE; one connector is one dedicated account whose key the owner issues at the consent screen). It is accepted with one real client, claude.ai's custom connector; a second client is not yet accepted. skills verify checks dependencies and CLI major/minor; it cannot prove an LLM loaded a Skill or resolve natural-language contradictions.
The control sync uses bounded workers (default one) and stays responsive during long handlers. Queued work rechecks stop, credentials and contracts at start. Graceful shutdown cancels unstarted work and waits for started handlers before run() returns; a stuck handler needs operator process management and business reconciliation. See the runbook (docs/運用_monban_導入と移転撤去_2026-10-03.md in the source) for private continuation cleanup, key exchange, safe relocation and retirement.
An unreleased portable work-record pilot (monban.workrecords) lives in the source only and is not part of this package.
日本語: 未リリースの「持ち運べる仕事の記録」は source にだけあり、この package には入っていない。
Metadata
Release files for monban 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| monban-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Release files / monban-1.0.0-py3-none-any.whl
| Download URL | monban-1.0.0-py3-none-any.whl |
|---|---|
| Size | 144.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
79634bce4ddfdc1adffc1c1c2b65bfe15c6c6f263bb1b7b27e4c66655f7aed28
|
|
BLAKE2b-256 checksum How to use checksums |
11969fbc03e27078e0432d29ffa50907eb399c574f02eb435ba127a736dde54a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for monban 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 1 file in this release has been uploaded with provenance metadata.
Attestation for monban-1.0.0-py3-none-any.whl
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.