BlindCouncil
Two people decide privately. Hermes delivers the shared outcome.
A release coordinator needs two reviewers to agree before calling a checklist approved. A group chat makes every answer public; private messages leave the coordinator checking identities, counting replies, chasing failed deliveries and reconstructing what survived a restart.
BlindCouncil puts that workflow inside Hermes. It sends separate Telegram requests, accepts one authenticated response per account and applies a persisted unanimous policy. The coordinator sees progress and the aggregate result; no model decides how a person voted.
BlindCouncil’s product vision is a privacy-conscious human authorization and decision engine for Hermes. Telegram is its first response channel. The current build implements authenticated decision coordination and deterministic outcomes; it has no agent-facing tools or protected action executor yet. Action-bound approval for one exact structured operation is being investigated separately. See the capability audit for implemented, tested and future boundaries.
Judge walkthrough · Genuine evidence · Native interface · Source · Architecture · Security
Status: working native Hermes/Telegram core and Desktop/Webapp interface. Browser creation/cancellation was tested in a token-free profile; the interface now also reads genuine Telegram history on the upgraded isolated gateway. Public GitHub/catalog, final visual review and demo video are pending. No public hosted demo is claimed.

Existing round 27cf6925d334, opened through the authenticated operator interface. Its two genuine callbacks and deliveries were verified earlier; this screenshot adds no new vote or action authorization. Capture provenance, genuine-history browser proof, dark variant.
Judges: start here
Shortest path without accounts: open the two-account native approval audit, then the real wrong-message/revocation denials and human-confirmed delivery recovery. These are sanitized records from actual gateway sessions and user button presses, not fixture responses. Provenance hashes preserve the exported originals; the records are local observations, not cryptographic attestations.
Run the actual workflow: install and configure one bot with two allowlisted accounts, keep one native gateway running, then:
hermes blindcouncil create --title "Release checklist" --expires 24h
hermes blindcouncil status --round-id ROUND_ID
- On Account 1's private request, press Approve. Status remains PENDING, with one response.
- On Account 2's own request, press Approve. The result becomes APPROVED and both authorized participants receive the aggregate.
- Press an already answered button again. It reports Your response is already recorded and adds no vote.
- To see rejection, create a fresh round and choose Approve on one account and Reject on the other. The result is REJECTED. Do not reuse or edit old responses.
The first response stays pending even if it is a rejection: this version collects both responses before resolving. The live outcomes above were already exercised; no judge needs to trust an AI-generated transcript. Approval, rejection, duplicate protection.
What is verified
| Behavior | Evidence | Test boundary |
|---|---|---|
| Two distinct configured accounts approve | Native audit | Genuine Telegram callbacks and human confirmation |
| Mixed responses reject; a repeated tap adds nothing | Rejection, duplicate | Genuine Telegram callbacks |
| Wrong message and revoked participant denied, with zero votes | Security audit | Genuine callbacks; decision-scoped authorization |
| Cancellation/expiry reject late responses | Cancellation, expiry | Genuine late button presses |
| Stored rounds survive a gateway restart | Before/after comparison | Actual native gateway stop/start |
| Aggregate reaches both participants | Delivery audit | Real Telegram deliveries, confirmed on both accounts |
| Blocked recipient recovers without resending the successful invitation | Forbidden, targeted retry, user confirmation | Real block/unblock; original error and exact deadline retained |
| Policy, rollback, migration, concurrency, delivery and operator RPC boundary | 64 passing methods, transcript | Actual Hermes SQLite; synthetic identities/transport/context; no Telegram network |
| Native Webapp creation, practical expiry, cancellation and history | Browser and stored-state proof | Real Hermes browser/RPC/SQLite; synthetic participants; no Telegram delivery claimed |
| Native interface reads real terminal outcomes; upgrade retains every database record | Genuine history, unchanged data/configuration | Read-only browser inspection after a controlled official v0.23.1 upgrade; no repeated human scenario |
| Authentication, invalid form and responsive widths | Browser checks | Anonymous protected API returns401; focused title error; desktop/tablet/phone fit; not a complete accessibility certification |
These are scoped integration proofs, not production load or adversarial security certification. A globally unallowlisted third account was not tested live. The owner used two accounts; distinct account identities do not establish independent humans. Real installation/removal/reinstallation retained all fixture records. Final compatibility/validation results are tracked in PROJECT_STATUS.md.
Why this belongs in Hermes
Hermes owns the gateway, bot credentials, configuration, plugin loading and persistent storage. BlindCouncil contributes the policy and coordination state through official interfaces:
| Hermes surface | BlindCouncil's job | Implementation |
|---|---|---|
| ctx.register_platform_handler("telegram", factory) | Receive native account/message identities and add only blindcouncil: callbacks | Native registration |
| ctx.spawn_task | Run owned notification work using Hermes' existing bot | Worker |
| plugins.plugin_storage.plugin_db() | Persist decisions, responses, bindings and retry history | Policy, schema |
| ctx.register_cli_command | Create, inspect, cancel and retry without a model call | Operator CLI |
| ctx.register_rpc and Desktop SDK host.request | Show permitted real state and operate decisions through an authenticated native page | Operator RPC, Desktop entry |
See Hermes' plugin guide and public API. No independent poller, core patch, private renderer modification or MCP server is needed.
The software replaces manual response bookkeeping: send both requests, associate each answer with the original private message, keep an immutable response, enforce a deadline, calculate the aggregate, recover queued work and show delivery failures. A prompt can describe those rules; the stored transactions and transport checks actually enforce them at this plugin's boundary. There is no model-facing vote-submission tool. Trusted host operators remain outside that boundary.
Architecture
flowchart LR
C[Coordinator CLI or native Desktop] --> D[Persistent decision + outbox]
D --> W[Owned native gateway worker]
W --> A[Account 1 private request]
W --> B[Account 2 private request]
A --> V[Identity + message + state checks]
B --> V
V --> P[Transactional unanimous policy]
P --> D
W --> R[Authorized aggregate notifications]
The durable queue retains retries and permanent failures. A message binding is committed before actionable buttons are attached. A terminal decision cannot become pending again. Details and limitations are in architecture and notification recovery.
Install and configure
Use a recent official Hermes installation with Telegram support. Install this repository at an explicitly reviewed 40-character commit:
hermes plugins install <GIT_URL> --ref <REVIEWED_40_CHARACTER_SHA> --enable --yes-deps
These placeholders are intentional until publication. --yes-deps consents to Hermes preparing the declared Python dependency; review it first. Interactive installation can ask instead. Hermes owns dependency management.
- Run
hermes gateway setupin your own terminal. Configure one Telegram bot and explicitly allowlist both participant IDs. Keep its token in Hermes' local secret configuration. - Both accounts must open that same bot and press Start. A bot cannot initiate a private conversation with an account that has not started it.
- Run
hermes blindcouncil configure. Enter the two distinct numeric account IDs locally. Account 1 is the coordinator and also a participant. No bot token is requested by this command. - Run
hermes blindcouncil readiness, then start your usualhermes gateway run. Run only one gateway poller for the bot.
The commands use your selected Hermes home/profile. For an experiment, set HERMES_HOME to a dedicated home before installation and setup. Do not copy an unrelated profile's secrets or change its settings.
For the graphical workspace, enable the Webapp locally and opt into BlindCouncil in Capabilities → Plugins. The Desktop/Webapp guide covers the private one-use sign-in link, WSL access and native controls. No inference call is needed to operate this interface.
Make a decision
hermes blindcouncil create \
--title "Approve the release checklist" \
--description "Both reviewers must confirm that the checklist is complete." \
--expires 24h
hermes blindcouncil status --round-id ROUND_ID
hermes blindcouncil history --limit 20
Each participant receives a separate private request with Approve and Reject buttons. One accepted response leaves the round PENDING. After both respond, two approvals produce APPROVED; a rejection produces REJECTED. Responses are immutable. This release collects both responses before resolving, including when the first response is a rejection.
Normal expiry choices are 15m, 1h, 24h, 3d and 7d; the default is 24 hours. The exact UTC deadline is persisted once and is never extended by a restart or retry. Short /bcprobe expire SECONDS durations exist solely for explicit integration tests.
hermes blindcouncil cancel --round-id ROUND_ID
hermes blindcouncil notifications --round-id ROUND_ID
hermes blindcouncil retry-notifications --round-id ROUND_ID
Inspect notification status before retrying. Retry queues only blocked work for that round. After expiry or cancellation an old invitation cannot be revived; a blocked terminal result is a different kind of message. Transient failures retry automatically with bounded backoff. Unblock the bot and press Start before retrying a Forbidden delivery. See notification recovery.
Privacy and authority
- Participants receive private requests and authorized aggregate outcomes. Operator CLI outputs expose progress and aggregates, not individual answers or account IDs.
- Two-person unanimity can permit someone to infer another person's answer from their own answer and the aggregate. This is controlled disclosure, not cryptographic secrecy.
- The trusted gateway operator, host and installed in-process code can access the local database. This is not end-to-end encryption, zero knowledge or tamper-proof storage.
- Two Telegram accounts prove distinct authenticated account identities, not two independent humans.
- BlindCouncil does not currently authorize or block other Hermes actions. Action-bound enforcement is planned. No Hermes-wide enforcement is claimed.
- Telegram may deliver duplicates after an uncertain network acknowledgement. Database mutations and logical notification work are idempotent; exactly-once external delivery is not promised.
Read security and data handling before using sensitive decisions.
Compatibility and development
Version 0.1.0, schema 3, Python 3.14.9 and python-telegram-bot 22.8. Hermes 0.23.1 or newer is the conservative declared floor. The native factory runs in process. Original live callbacks used source 2c955c3a8c0bf8f52cbfad13ed3215ade4eb6f94; the primary gateway now runs the official v0.23.1 tag 3b421578588243f920886e6fe79a6faa87ea026e. Its preserved genuine history passes browser checks; no new callback on that version is implied. 64-method source inventory, Doctor, dependency-prepared validation.
hermes plugins doctor . --ci
hermes plugins validate . --install-deps
python3 scripts/test.py --help
The supporting runner reuses an existing Hermes-managed environment and creates temporary credential-free homes. It never starts Telegram polling or loads the live secret configuration. Troubleshooting covers failed delivery, authorization and recovery.
__init__.py native Hermes loader entry
plugin.yaml metadata, settings and pinned Python dependency
blindcouncil/ native handlers, policy, lifecycle, outbox and worker
desktop/plugin.js official native page, navigation and permitted RPC controls
tests/ supporting policy, security and recovery regressions
scripts/test.py isolated test runner
docs/evidence/ sanitized original live proofs and supporting results
SECURITY.md trust, disclosure, retention and deletion
PROJECT_MASTER_PLAN.md / ROADMAP.md / HANDOFF.md durable scope and continuation
The native interface is functional and browser-tested, including genuine history. Manual Windows access, full contrast/theme/accessibility review, final identity and video remain open. On narrow screens the selected detail follows the history list. Agent-callable authorization requests, a fail-closed action executor, additional policies and platforms remain planned. Only two-person collect-both unanimity exists today. The scoped action investigation considers an exact bounded release note in plugin-owned storage; no grant or execution test exists. Current aggregate messages do not authorize other Hermes actions. The master plan and roadmap preserve all future scope without claiming it shipped.
MIT licensed: LICENSE. No paid model API or hosted database is required by the implemented workflow. The plugin does not depend on a Vercel site.
README from the reviewed commit 2a7ceb4; it updates when the author re-pins.

sopskistar