Summary
OpenAB is great as a Discord-to-Claude bridge, but there's a broader use case: using OpenAB as a unified agent broker for both human (Discord) and machine (scheduler/CI) triggers, sharing the same session pool and ACP pipeline.
This issue proposes adding an HTTP trigger option so that automated tasks (cron jobs, schedulers, CI pipelines) can invoke the same agent backend that Discord users interact with — without needing Discord as a dependency.
Motivation
Current state
Discord @mention ──▶ OpenAB ──▶ ACP stdio ──▶ Claude Code / Kiro / Codex
Works perfectly for human-driven interactions. But for automated tasks (e.g. "generate a daily newsletter digest at 07:12 every morning"), Discord is an awkward dependency:
- Scheduler must send a Discord message and poll the thread for completion
- No structured return value (exit code, output text)
- Internet dependency for what is essentially a local task
Proposed state
Discord @mention ──┐
├──▶ OpenAB ──▶ ACP stdio ──▶ Claude Code / Kiro / Codex
HTTP POST /v1/prompt ──┘ (shared session pool)
Both triggers share the same session pool and ACP backend. HTTP callers get a synchronous JSON response; Discord users get the existing streaming thread experience.
Proposed Design
New [http] config block
[http]
enabled = true
port = 7865
bind = "127.0.0.1" # localhost-only by default
token = "${ACP_HTTP_TOKEN}" # Bearer token auth (64-char hex)
timeout_ms = 300000
New endpoint: POST /v1/prompt
Request:
{
"prompt": "Generate a summary of today's newsletters",
"timeout_ms": 300000,
"caller": "my-scheduler/v1"
}
Response:
{
"output": "## Daily Digest\n...",
"success": true,
"duration_ms": 8432,
"timed_out": false
}
Auth: Authorization: Bearer <token>
Health check: GET /health (no auth required)
Implementation sketch
// src/http.rs (new file, ~80 lines)
use axum::{routing::{get, post}, Router, Json, extract::State};
use crate::acp::pool::SessionPool;
async fn handle_prompt(
State(pool): State<Arc<SessionPool>>,
headers: HeaderMap,
Json(req): Json<PromptRequest>,
) -> Json<PromptResponse> {
// 1. verify Bearer token
// 2. acquire session from pool (or create ephemeral one)
// 3. submit prompt via ACP stdio
// 4. collect response until "done" event
// 5. return JSON
}
pub fn router(pool: Arc<SessionPool>, token: String) -> Router {
Router::new()
.route("/v1/prompt", post(handle_prompt))
.route("/health", get(health))
.with_state(pool)
}
// src/main.rs — spawn HTTP server alongside Discord gateway
if config.http.enabled {
let http_router = http::router(pool.clone(), config.http.token.clone());
tokio::spawn(axum::serve(
TcpListener::bind(format!("{}:{}", config.http.bind, config.http.port)).await?,
http_router,
));
}
Additional use case: SSH/OpenShell backend
For users running OpenAB locally (Mac mini, home server) with OpenShell sandboxes, it would be useful to support an SSH-based backend in addition to direct subprocess spawning:
[agent]
command = "ssh"
args = ["openshell-claude-dev", "claude-agent-acp"]
working_dir = "/sandbox"
OpenShell auto-manages SSH config (~/.ssh/config), so no key management is needed. This gives sandboxed execution (Landlock filesystem + network policy) without changing OpenAB's ACP stdio protocol.
Benefits
|
Current (Discord only) |
With HTTP trigger |
| Scheduler/CI invocation |
❌ Need Discord message + polling |
✅ POST /v1/prompt |
| Return value |
❌ Parse Discord thread |
✅ Structured JSON |
| Internet dependency |
✅ Required |
✅ HTTP works localhost-only |
| Multi-turn (human) |
✅ Discord threads |
✅ Unchanged |
| Session pool sharing |
N/A |
✅ Both triggers share pool |
| Auth |
Discord OAuth |
Bearer token (localhost) |
Scope
This is intentionally minimal:
- New file:
src/http.rs (~80 lines)
- Modified:
src/config.rs (add [http] block), src/main.rs (spawn HTTP server)
- No changes to existing Discord behavior
- No new dependencies beyond
axum (already common in Rust ecosystem)
Questions for maintainers
- Is this direction aligned with OpenAB's goals? (universal agent broker vs. Discord-specific tool)
- Would you prefer a separate crate/binary for the HTTP server, or integrated?
- Any concerns about the Bearer token auth model for localhost use?
Happy to submit a PR if the direction looks good.
Summary
OpenAB is great as a Discord-to-Claude bridge, but there's a broader use case: using OpenAB as a unified agent broker for both human (Discord) and machine (scheduler/CI) triggers, sharing the same session pool and ACP pipeline.
This issue proposes adding an HTTP trigger option so that automated tasks (cron jobs, schedulers, CI pipelines) can invoke the same agent backend that Discord users interact with — without needing Discord as a dependency.
Motivation
Current state
Works perfectly for human-driven interactions. But for automated tasks (e.g. "generate a daily newsletter digest at 07:12 every morning"), Discord is an awkward dependency:
Proposed state
Both triggers share the same session pool and ACP backend. HTTP callers get a synchronous JSON response; Discord users get the existing streaming thread experience.
Proposed Design
New
[http]config blockNew endpoint:
POST /v1/promptRequest:
{ "prompt": "Generate a summary of today's newsletters", "timeout_ms": 300000, "caller": "my-scheduler/v1" }Response:
{ "output": "## Daily Digest\n...", "success": true, "duration_ms": 8432, "timed_out": false }Auth:
Authorization: Bearer <token>Health check:
GET /health(no auth required)Implementation sketch
Additional use case: SSH/OpenShell backend
For users running OpenAB locally (Mac mini, home server) with OpenShell sandboxes, it would be useful to support an SSH-based backend in addition to direct subprocess spawning:
OpenShell auto-manages SSH config (
~/.ssh/config), so no key management is needed. This gives sandboxed execution (Landlock filesystem + network policy) without changing OpenAB's ACP stdio protocol.Benefits
POST /v1/promptScope
This is intentionally minimal:
src/http.rs(~80 lines)src/config.rs(add[http]block),src/main.rs(spawn HTTP server)axum(already common in Rust ecosystem)Questions for maintainers
Happy to submit a PR if the direction looks good.