Three extension layers
docs/PLUGIN-SPEC.md (v2+): hook arguments, env vars, permissions.
API — plugin-api.html / docs/PLUGIN-API.md.
How-to — plugin-how-to.html / docs/PLUGIN-HOW-TO.md.
brain.py runs scheduler.py against memory/schedules.json. No OS cron required. Plugin cron hooks merge in via furoshiki plugins reload.PLUGIN.md directories under $FUROSHIKI_DATA/plugins/. Seven hook types. All hooks run as isolated subprocesses with scoped API tokens — no direct DB access. Plugins may declare settings_schema for JSON settings, or operators can edit the full PLUGIN.md in Dashboard → Work & extensions → Plugins (manifest knobs, hooks, permissions).extension_work_items. Dashboard → Extensions + furoshiki extensions ….Seven ways to extend
Each hook fires at a specific point in Furoshiki's lifecycle. Declare hooks in your PLUGIN.md and provide a Python entry script. Hooks run as subprocesses; the correct --mode and argument paths are passed automatically. Plugins with a settings_schema block get a live settings form in Dashboard → Work & extensions → Plugins — no hand-editing JSON required.
schedules.json on reload and runs via run_plugin_scheduled.py. Stdout mind_queue_item lines are mediated.{"label","content","priority"} to inject context the model will see.{"label","content"} to add context to the reflection prompt.name, description, when_to_use, and args_schema. Return any JSON object.conversation_mode or append a prompt_addendum.Plugin execution pipeline
Every hook — scheduled or event-driven — goes through the same isolation path. The core never imports plugin code; the plugin never imports core modules.
Token lifecycle
plugin_loader calls issue_plugin_token(name, permissions, timeout_sec) → writes row to plugin_tokens table with TTL = hook timeout + 30 s.FUROSHIKI_PLUGIN_API_TOKEN and FUROSHIKI_PLUGIN_API_URL are set in the subprocess environment. FUROSHIKI_API_TOKEN (admin key) is explicitly removed.plugin_tokens and checks the declared permission scope. Write endpoints (mind-queue, llm) require the matching permission flag.{"type":"mind_queue_item",...} NDJSON lines and inserts them into mind_queue if output_mind_queue permission is declared.revoke_plugin_token(token) deletes the row. Expired tokens are also pruned automatically on each new issue.Isolation model
Plugins are guests. The system is designed so that a buggy or malicious plugin cannot corrupt Furoshiki's state, impersonate the operator, or bypass the voice dispatch gate.
scripts/ is stripped from PYTHONPATH. Plugins cannot import db, import llm, or reach any Furoshiki internal module. Only the Plugin SDK and the plugin's own scripts/ are available.sqlite3.connect(), longmemory.sqlite, FUROSHIKI_DATA path constructions, or from db import.sqlite3 for their own data under $FUROSHIKI_PLUGIN_DATA_DIR. The restriction applies only to Furoshiki's own databases.mind_queue inserts go through POST /api/v1/plugin/mind-queue. The loader also mediates mind_queue_item NDJSON in stdout. Direct DB writes are structurally impossible.mind_queue → voice_dispatcher. Plugins cannot send Telegram messages directly or bypass the voice dispatch gate.max_priority in their manifest. The operator sets a global cap in operator_settings.json (default: critical). Both caps are enforced; the more restrictive wins. bypass (skips DND, fires pulse immediately) requires explicit operator opt-in.Plugin notification priority tiers
| Priority | DND | Other gates | Dispatch latency |
|---|---|---|---|
bypass | Ignored | All skipped | Immediate — outreach_trigger.fire_now() on insert |
critical | Respected | All skipped | ≤ 5 min (next pulse) |
urgent | Respected | Standard cooldowns | ≤ 5 min |
high | Respected | Standard cooldowns | ≤ 5 min |
normal | Respected | Standard cooldowns | varies |
low | Respected | Standard cooldowns | varies |
bypass and critical items are never expired by topic dedup or the 24-hour background sweep. Use bypass only for genuine emergencies. Declare max_priority: bypass in the manifest and ensure the operator has set plugins.max_priority_cap: bypass.
Observability
Hook and callable activity is written to Logs → Extensions in the operator dashboard (log_store rows with structured context: discovery, hook phases, subprocess invoke start/end, callable match attempts and outcomes). Process logs still include grep-friendly plugin_pipeline step=… lines. Callable manifest discovery is throttled when unchanged so high-frequency callers do not flood the log DB.
Permissions reference
Declare only the permissions your plugin actually needs. The Plugin API validates every write operation against the token's declared scope.
| Permission flag | Unlocks |
|---|---|
read_user_facts: true | Semantic search over ChromaDB user_facts via GET /api/v1/plugin/user-facts |
output_mind_queue: true | Insert into mind_queue via API (POST /api/v1/plugin/mind-queue) or mediated stdout. Priority is capped by the plugin's declared max_priority (default high) and the operator global cap (default critical). bypass-priority items trigger an immediate pulse via outreach_trigger.fire_now(). |
llm: true | LLM calls via POST /api/v1/plugin/llm — budget enforced, source tagged |
network: true | Outbound network from the plugin subprocess (declared intent only; not enforced at kernel level) |
openrouter_web: true | Sets FUROSHIKI_PLUGIN_OPENROUTER_WEB=1 so the LLM endpoint may use OpenRouter :online |
All read endpoints (/state, /emotion-history, /recent-memories, /budget-summary) and /log are available to any valid plugin token regardless of declared permissions.
Delegations, repairs & proposals
extension_work_items is the unified SQLite view for all agentic work. Installing a plugin from a proposal is distinct — furoshiki plugins apply moves files into plugins/; the queue tracks progress and phases.
research · code · self-mod
doctor watchdog
self_modification_dispatcher
Quick reference
| Command | What it does |
|---|---|
furoshiki plugins list | List installed plugins and their active status |
furoshiki plugins show <name> | Full manifest + hook list for one plugin |
furoshiki plugins apply <proposal> | Install an approved plugin proposal into ~/.furoshiki/plugins/ |
furoshiki plugins enable/disable <name> | Toggle active: true/false in PLUGIN.md |
furoshiki plugins reload | Merge cron hooks into schedules.json — run after any PLUGIN.md edit |
furoshiki plugins test-suite | Run hook-test-suite plugin against all 7 hook types, print pass/fail |
furoshiki plugins delete <name> --yes | Permanently remove a plugin directory |
furoshiki extensions plugins … | Same subcommands under the extensions hub alias |
furoshiki start | Start Brain + Doctor (scheduler + API server live) |