-
Notifications
You must be signed in to change notification settings - Fork 2
docs: channel setup guides with compile-tested TOML examples #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7e3b8e4
86553af
0075027
f840a43
3d545f9
dbadd77
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -1542,4 +1542,219 @@ weight = 20 | |||||||
| "error should name the missing field, got: {msg}" | ||||||||
| ); | ||||||||
| } | ||||||||
|
|
||||||||
| // ── Channel config example tests ───────────────────────────────────────── | ||||||||
| // | ||||||||
| // Each test below does two things: | ||||||||
| // | ||||||||
| // 1. Parses the inline TOML example that appears in the corresponding | ||||||||
| // docs/channels/*.md setup guide, verifying the example is | ||||||||
| // syntactically valid and matches the live ChannelConfig schema. | ||||||||
| // | ||||||||
| // 2. Loads the markdown file via include_str! and scans every fenced | ||||||||
| // ```toml block that contains [[channels]], wrapping each in a | ||||||||
| // minimal CalciforgeConfig envelope and parsing it. If any | ||||||||
| // [[channels]] block in the doc drifts out of sync with the schema, | ||||||||
| // cargo test catches it here. | ||||||||
| // | ||||||||
| // When you add or rename a ChannelConfig field, run `cargo test -p calciforge` | ||||||||
| // and fix any failures in these tests before updating the docs. | ||||||||
|
|
||||||||
| fn extract_toml_blocks(markdown: &str) -> Vec<String> { | ||||||||
| let mut blocks = Vec::new(); | ||||||||
| let mut in_block = false; | ||||||||
| let mut current = String::new(); | ||||||||
| for line in markdown.lines() { | ||||||||
| if line.trim() == "```toml" { | ||||||||
| in_block = true; | ||||||||
| current.clear(); | ||||||||
| } else if line.trim() == "```" && in_block { | ||||||||
| in_block = false; | ||||||||
| blocks.push(current.clone()); | ||||||||
| } else if in_block { | ||||||||
| current.push_str(line); | ||||||||
| current.push('\n'); | ||||||||
| } | ||||||||
| } | ||||||||
|
||||||||
| } | |
| } | |
| assert!(!in_block, "unterminated ```toml block"); |
Copilot
AI
Apr 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These doc tests will pass if channel_blocks_from_doc(doc) returns an empty Vec (e.g., if the [[channels]] example block is accidentally removed or extract_toml_blocks fails to detect it). Recommend capturing let blocks = channel_blocks_from_doc(doc); then assert!(!blocks.is_empty(), "no [[channels]] TOML blocks found") before iterating (same applies to the other test_channel_docs_* tests below).
Copilot
AI
Apr 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These doc-validation tests index cfg.channels[0], which produces a less-informative panic if a fenced TOML block contains the string [[channels]] (e.g. in a comment) but doesn’t actually deserialize into any channel entries. Also, if a single fenced block contains multiple [[channels]] entries, only the first one is checked. Consider asserting cfg.channels.len() == 1 (or at least !cfg.channels.is_empty()) and validating the kind for all parsed channel entries; same applies to the other test_channel_docs_*_toml_blocks_valid loops.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| --- | ||
| layout: default | ||
| title: Matrix Channel Setup | ||
| --- | ||
|
|
||
| # Matrix Channel | ||
|
|
||
| Calciforge connects to Matrix via the [Client-Server API v3](https://spec.matrix.org/v1.9/client-server-api/) | ||
| using **HTTP long-polling** (`/sync`). No webhook endpoint or open firewall port required. | ||
|
|
||
| > **No end-to-end encryption.** The Matrix channel sends and receives plaintext `m.text` | ||
| > events only. E2EE is not supported due to compile-time dependency conflicts in the | ||
| > current workspace. Do not use this channel in rooms where E2EE is required. | ||
|
|
||
| ## Architecture | ||
|
|
||
| ``` | ||
| Matrix user ──→ homeserver ──→ Calciforge (/sync long-poll) | ||
| │ | ||
| identity resolution | ||
| (allowed_users check) | ||
| agent dispatch | ||
| │ | ||
| Matrix user ←── homeserver ←── Calciforge (PUT /send/m.room.message) | ||
| ``` | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| 1. **Register a Matrix account** for the bot on your homeserver (or matrix.org for testing). | ||
| The account does not need to be a human account — a plain `@calciforge-bot:example.com` | ||
| works fine. | ||
| 2. **Generate an access token** for that account: | ||
|
|
||
| ```bash | ||
| curl -s -X POST 'https://matrix.example.com/_matrix/client/v3/login' \ | ||
| -H 'Content-Type: application/json' \ | ||
| -d '{ | ||
| "type": "m.login.password", | ||
| "user": "@calciforge-bot:example.com", | ||
| "password": "botpassword" | ||
| }' | grep access_token | ||
| ``` | ||
|
|
||
| Copy the `access_token` value from the response. | ||
|
|
||
| 3. **Find the room ID** for the room you want the bot to listen in: | ||
| - In most clients: room settings → Advanced → Internal room ID | ||
| - Format: `!abc123def456:example.com` | ||
| - The bot will auto-accept room invites from users listed in `allowed_users` | ||
|
|
||
| ## Step 1: Save the access token | ||
|
|
||
| ```bash | ||
| install -m 600 /dev/null ~/.calciforge/secrets/matrix-token | ||
| printf '%s' 'syt_YOUR_ACCESS_TOKEN_HERE' > ~/.calciforge/secrets/matrix-token | ||
| ``` | ||
|
|
||
| ## Step 2: Channel config | ||
|
|
||
| Add to `~/.calciforge/config.toml`: | ||
|
|
||
| ```toml | ||
| [[channels]] | ||
| kind = "matrix" | ||
| enabled = true | ||
| homeserver = "https://matrix.example.com" | ||
| access_token_file = "~/.calciforge/secrets/matrix-token" | ||
| room_id = "!abc123def456:example.com" | ||
| allowed_users = ["@operator:example.com"] | ||
| ``` | ||
|
|
||
| | Field | Required | Description | | ||
| |---|---|---| | ||
| | `homeserver` | yes | Full URL of the Matrix homeserver | | ||
| | `access_token_file` | yes | Path to file containing the bot's access token | | ||
| | `room_id` | yes | Internal room ID (starts with `!`) | | ||
| | `allowed_users` | yes | Matrix user IDs permitted to send commands; use `["*"]` to allow all room members; empty list is rejected at startup | | ||
| | `scan_messages` | no (`false`) | Enable inbound adversarial content scanning | | ||
| | `allow_chat_secret_set` | no (`false`) | Allow `!secure set` via Matrix (not recommended) | | ||
|
|
||
|
Comment on lines
+72
to
+80
|
||
| ## Step 3: Identity config | ||
|
|
||
| The alias `id` is the full Matrix user ID including homeserver: | ||
|
|
||
| ```toml | ||
| [[identities]] | ||
| id = "operator" | ||
| display_name = "Alice" | ||
| role = "admin" | ||
| aliases = [ | ||
| { channel = "matrix", id = "@alice:example.com" }, | ||
| ] | ||
|
|
||
| [[routing]] | ||
| identity = "operator" | ||
| default_agent = "librarian" | ||
| allowed_agents = ["librarian"] | ||
| ``` | ||
|
|
||
| Messages from Matrix users not in `allowed_users` are ignored before identity resolution. | ||
| Messages from `allowed_users` members with no matching identity alias are also dropped. | ||
|
|
||
| ## Step 4: Invite the bot | ||
|
|
||
| Invite `@calciforge-bot:example.com` to the room. Calciforge will auto-accept the invite | ||
| if the inviting user's Matrix ID is in `allowed_users`. | ||
|
|
||
| ## Step 5: Verify | ||
|
|
||
| ```bash | ||
| calciforge doctor # validates config | ||
| calciforge # start; send a message in the room | ||
| ``` | ||
|
|
||
| The bot responds to commands (`!help`, `!ping`, `!agents`, etc.) and routes other messages | ||
| to the default agent for the sender's identity. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
extract_toml_blocksonly enters TOML mode when a line trims to exactly```toml. This misses valid Markdown fences like```tomlwith trailing spaces or extra info-string attributes (which would silently skip blocks and reduce the effectiveness of the doc/schema sync). Consider loosening the check tostarts_with("```toml")(after trimming) so the extraction matches common Markdown variations.