bastion/channel-scaffold
Skill8 starsChanged 4 months agoMITView on GitHub
Scaffolds a new Bastion channel skill: generates the SKILL.md stub and documents the Channel trait requirements, bastion.toml registration, and OwnerMap auth rules.
- Reads credentials
In this repository17 other files it gives its agents.
- bastion/agentskills-installSkill
- bastion/agentskills-publishSkill
- bastion/calendarSkill
- bastion-pet-packSkill
- bastion/crisis-modeSkill
- bastion/life-logSkill
- mcporterSkill
- bastion/mesh-connectSkill
- bastion/mobile-connectSkill
- bastion/onboardingSkill
- bastion/output-validatorSkill
- bastion/persona-engineSkill
- bastion/proactive-engineSkill
- bastion/self-improvingSkill
- bastion/skill-writerSkill
- bastion/weekly-reviewSkill
- bastion/weight-systemSkill
The file
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 5 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 15
Use this skill to create a new Bastion channel skill.
- Distinctive: line 28
Every Bastion channel MUST satisfy all three rules:
- Very specific: line 94
When scaffolding /add-<channel>, generate:
- Very specific: line 163
Add [[channel]] type = "whatsapp" to bastion.toml
- Very specific: line 164
Create skills/add-whatsapp/SKILL.md using the stub above
- Distinctive: line 175
Before merging any community channel:
- Very specific: line 177
Verify OwnerMap.resolve(token) is called in every handler that modifies state (CR-03)
- Distinctive: line 178
Verify no provider is called directly (only via AgentHandle)
- Distinctive: line 179
Verify API keys are read from env vars, not hardcoded
- Distinctive: line 180
Verify webhook signatures are validated before processing payloads
- Very specific: line 188
Channel type slug collision: if bastion.toml [[channel]] type matches an existing built-in, the daemon will use the built-in.
- Very specific: line 190
Channel startup failure: if channel.run() returns an error, the daemon logs it and continues running other channels.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
bastion/channel-scaffold
Use this skill to create a new Bastion channel skill. Channels are skills, not core — they implement the Channel trait and register via bastion.toml. Specific channels (WhatsApp, Discord, Email) are community and future work; this skill provides the scaffold and documentation only.
Objective
Generate a skills/add-<channel>/ directory with a SKILL.md stub and document the three
mandatory rules every channel implementation must follow.
Channel Trait Requirements
Every Bastion channel MUST satisfy all three rules:
Rule 1: Implement the Channel trait (src/channel/mod.rs)
#[async_trait::async_trait]
pub trait Channel: Send + Sync {
/// Run the channel's I/O loop forever. Each inbound message is sent to the AgentLoop;
/// the reply is returned over the channel's transport.
async fn run(self: Box<Self>, agent: AgentHandle) -> anyhow::Result<()>;
/// Optional default persona hint for messages arriving on this channel (CHAN-04).
fn default_persona(&self) -> Option<&str>;
}Both methods are required. run owns the I/O loop; default_persona returns the persona
hint sent to the router for messages on this channel (return None if no preference).
Rule 2: Register via bastion.toml [[channel]]
Channels are not auto-discovered. Each channel must have a [[channel]] entry in
bastion.toml. The daemon reads these at startup and instantiates the matching type.
[[channel]]
type = "whatsapp" # matches the Rust type slug registered in main.rs
config = { phone = "+55..." } # channel-specific config passed to the constructorRule 3: Never call providers directly — route all messages through AgentHandle
Channels are transport adapters, not reasoning engines. All LLM calls, memory reads,
and skill dispatches must flow through AgentHandle. A channel that calls a provider
directly violates the architecture boundary and breaks persona routing.
// Correct — forward to AgentHandle
let reply = agent.send(Message::new(owner_id, text)).await?;
// Wrong — never do this from a channel
let reply = openai_client.complete(prompt).await?;Rule 4: HTTP handlers MUST use OwnerMap.resolve(token) → 401 (CR-03)
Any HTTP handler in a channel that accepts requests from untrusted clients must resolve the caller's identity from a trusted token map — never from the request body.
// CR-03 pattern — mandatory for any HTTP channel handler
let owner_id = owner_map
.resolve(&token)
.ok_or(StatusCode::UNAUTHORIZED)?; // 401 if unknown token
// Wrong — request body controls identity (privilege escalation risk)
let owner_id = body.owner_id; // NEVER do thisThis is the same pattern used by WebhookChannel (see src/channel/webhook.rs).
Community channel developers who skip CR-03 create an authentication bypass.
Generated Stub Structure
When scaffolding /add-<channel>, generate:
skills/
add-<channel>/
SKILL.md ← conversational skill (this stub)The SKILL.md stub guides the user through wiring the new channel in Rust and registering it.
The Rust implementation lives in src/channel/<channel>.rs (outside this skill's scope).
SKILL.md Stub Template
---
name: bastion/add-<channel>
version: "1.0.0"
description: >
Guides setup of the <Channel> channel for Bastion: installation, authentication,
and bastion.toml registration.
triggers:
- /add-<channel>
- "configurar <channel>"
---
# bastion/add-<channel>
Sets up the <Channel> integration so Bastion can receive and send messages via <Channel>.
## Prerequisites
- [ ] <Channel> account and API credentials
- [ ] Bastion daemon v1.0+ running
## Setup Steps
1. **Obtain credentials**: [channel-specific auth steps]
2. **Add to bastion.toml**:
```toml
[[channel]]
type = "<channel>"
config = { api_key = "<YOUR_API_KEY>" }- Restart daemon:
systemctl restart bastion(orcargo runfor dev) - Test: send a message via
<Channel>; Bastion should reply.
Implementation Notes (for contributors)
- Implement
Channeltrait insrc/channel/<channel>.rs - Use
OwnerMap.resolve(token)for any HTTP callback — never trust request body identity (CR-03) - Route all messages through
AgentHandle— do not call providers directly - Register the type slug in
main.rschannel factory
Security
- Store API keys in environment variables, never in bastion.toml plaintext
- Any webhook endpoint must validate the channel's signature header before processing
- Apply CR-03 pattern: resolve owner from trusted map, return 401 for unknown credentials
---
## Example: Community WhatsApp Channel
A community developer building WhatsApp support would:
1. Implement `WhatsAppChannel` in `src/channel/whatsapp.rs` (implements Channel trait)
2. Add `[[channel]] type = "whatsapp"` to bastion.toml
3. Create `skills/add-whatsapp/SKILL.md` using the stub above
4. Submit the skill to agentskills.io for community review
The WhatsApp channel does NOT ship in Bastion core (D-11). It is community work that follows
this scaffold. Core ships only `WebhookChannel` (HTTP/SSE) and `TelegramChannel`.
---
## Security Reminder
New HTTP handlers introduced by a community channel inherit the daemon's trust surface.
Before merging any community channel:
1. Verify `OwnerMap.resolve(token)` is called in every handler that modifies state (CR-03)
2. Verify no provider is called directly (only via AgentHandle)
3. Verify API keys are read from env vars, not hardcoded
4. Verify webhook signatures are validated before processing payloads
Channels that omit these checks create privilege escalation vectors (T-06-04-03).
---
## Edge Cases
- **Channel type slug collision**: if `bastion.toml [[channel]] type` matches an existing built-in, the daemon will use the built-in. Use a distinct slug (e.g., `community-whatsapp`).
- **Multiple instances of the same channel**: Bastion supports multiple `[[channel]]` blocks with the same type but different configs (e.g., two Telegram bots). Each gets its own AgentHandle clone.
- **Channel startup failure**: if `channel.run()` returns an error, the daemon logs it and continues running other channels. One broken channel does not take down the daemon.Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

