v0.0.28: BotFather-only registration, per-instance bot toggle, docs update

Security:
- Bot registration restricted to BotFather (requires botfather_token)
- Direct POST /v1/bot/register without BotFather auth → rejected

Deploy:
- systemd service reads /home/warzone/server.env for EXTRA_ARGS
- deploy/warzone-server.env.mequ: no bots (default)
- deploy/warzone-server.env.kh3rad3ree: --enable-bots
- setup.sh copies per-hostname env file

Docs updated:
- LLM_HELP.md: BotFather flow, plaintext bot messaging, E2E option, bridge
- LLM_BOT_DEV.md: botfather_token requirement, E2E mode, bridge section
- BOT_API.md: full BotFather flow, ownership, numeric IDs, webhook delivery
- SERVER.md: --enable-bots flag, per-instance config, bot system section
- USAGE.md: bot messaging, BotFather, bridge tool

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-03-29 09:52:12 +04:00
parent 8603087afb
commit 76cac77259
14 changed files with 288 additions and 74 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "warzone-protocol"
version = "0.0.27"
version = "0.0.28"
edition = "2021"
license = "MIT"
description = "Core crypto & wire protocol for featherChat (Warzone messenger)"

View File

@@ -236,6 +236,8 @@ struct RegisterBotRequest {
e2e: Option<bool>, // true = E2E bot, false/None = plaintext bot
#[serde(default)]
owner: Option<String>, // fingerprint of the bot creator
#[serde(default)]
botfather_token: Option<String>,
}
/// Register a bot and receive a token.
@@ -249,12 +251,25 @@ async fn register_bot(
State(state): State<AppState>,
Json(req): Json<RegisterBotRequest>,
) -> AppResult<Json<serde_json::Value>> {
// TODO: In production, only @botfather should be able to register bots.
// For v1, direct registration is allowed for development.
if !state.bots_enabled {
return Ok(Json(serde_json::json!({"ok": false, "description": "Bot API is disabled on this server. Use a server with --enable-bots"})));
}
// Only BotFather can register bots
// Require botfather_token field matching the stored BotFather token
if let Some(ref bf_token) = req.botfather_token {
let botfather_fp = "0000000000000000botfather00000000";
let bf_key = format!("bot_fp:{}", botfather_fp);
let stored_token = state.db.tokens.get(bf_key.as_bytes())
.ok().flatten()
.map(|v| String::from_utf8_lossy(&v).to_string());
if stored_token.as_deref() != Some(bf_token.as_str()) {
return Ok(Json(serde_json::json!({"ok": false, "description": "invalid BotFather token"})));
}
} else {
return Ok(Json(serde_json::json!({"ok": false, "description": "bot registration requires BotFather authorization. Message @botfather to create a bot."})));
}
let fp = req
.fingerprint
.chars()

View File

@@ -50,7 +50,7 @@ async fn pwa_manifest() -> impl IntoResponse {
async fn service_worker() -> impl IntoResponse {
([(header::CONTENT_TYPE, "application/javascript")], r##"
const CACHE = 'wz-v9';
const CACHE = 'wz-v10';
const SHELL = ['/', '/wasm/warzone_wasm.js', '/wasm/warzone_wasm_bg.wasm', '/icon.svg', '/manifest.json'];
self.addEventListener('install', e => {
@@ -241,7 +241,7 @@ let pollTimer = null;
let ws = null; // WebSocket connection
let wasmReady = false;
const VERSION = '0.0.27';
const VERSION = '0.0.28';
let DEBUG = true; // toggle with /debug command
// ── Receipt tracking ──