--- a/crates/airouter-agentd/setup/runtime_grants.sql +++ b/crates/airouter-agentd/setup/runtime_grants.sql @@ -15,5 +15,6 @@ GRANT SELECT, INSERT ON agent_task_events TO airouter_framework; GRANT SELECT, INSERT ON agent_controller_bindings TO airouter_framework; GRANT SELECT ON agent_runtime_contract TO airouter_framework; +GRANT SELECT, INSERT, UPDATE ON agent_orchestration_decisions TO airouter_framework; GRANT USAGE ON SEQUENCE agent_tasks_sequence_seq TO airouter_framework; -- No DELETE, DDL, migration-ledger, or cross-service SQL access. --- a/crates/airouter-catalog/src/bin/control.rs +++ b/crates/airouter-catalog/src/bin/control.rs @@ -29,7 +29,7 @@ output_schema: spec["outputSchema"].clone(), examples: vec![], source_ref: format!("/srv/airouter/crates/{name}/src/lib.rs"), - documentation: format!("/srv/airouter/crates/{name}/README.md"), + documentation: format!("/usr/share/airouter/docs/{name}/README.md"), }); } let source_revision = format!( --- a/crates/airouter-catalog/src/bin/development.rs +++ b/crates/airouter-catalog/src/bin/development.rs @@ -19,7 +19,7 @@ input_schema: schema, output_schema: json!({"type":"object","description":"Existing versioned result/evidence contract. Use the documented library or CLI with host-authorized inputs; discovery grants no execution authority."}), examples: vec![], source_ref: format!("/srv/airouter/crates/{engine}/src/{}", match name { "rust_check" => "rust.rs", "workspace_permissions" => "permissions.rs", _ => "lib.rs" }), - documentation: format!("/srv/airouter/crates/{engine}/README.md"), + documentation: format!("/usr/share/airouter/docs/{engine}/README.md"), }).collect(); for (id, kind, engine, version, description, schema, source) in [ ("tool.graphify_project", Kind::Tool, "airouter-graphify", airouter_graphify::VERSION, @@ -35,7 +35,7 @@ tags: vec!["development".into(), "shared".into(), "host-binding".into()], input_schema: schema, output_schema: json!({"type":"object","description":"Documented result/evidence contract; discovery does not grant application scope, execution or model authority."}), examples: vec![], source_ref: format!("/srv/airouter/crates/{engine}/src/{source}"), - documentation: format!("/srv/airouter/crates/{engine}/README.md"), + documentation: format!("/usr/share/airouter/docs/{engine}/README.md"), }); } Snapshot { --- a/crates/airouter-catalog/src/bin/host.rs +++ b/crates/airouter-catalog/src/bin/host.rs @@ -21,7 +21,7 @@ output_schema: spec["outputSchema"].clone(), examples: vec![], source_ref: "/srv/airouter/crates/airouter-host/src/lib.rs".into(), - documentation: "/srv/airouter/crates/airouter-host/README.md".into(), + documentation: "/usr/share/airouter/docs/airouter-host/README.md".into(), }; let source_revision = format!( "{:x}", --- a/crates/airouter-catalog/src/bin/learning.rs +++ b/crates/airouter-catalog/src/bin/learning.rs @@ -10,7 +10,7 @@ title:"learning_report".into(), description:"Summarize bounded tool outcomes, repeated work, usage/context and exact-version adoption. Host supplies the application collector, profile and observation scope; Workbench compatibility interface: workbench/tool.learning_report. Evidence only; no inference or automatic effects.".into(), executor:"airouter-learning".into(), tags:vec!["learning".into(),"shared".into(),"application-binding".into()], input_schema:airouter_learning::report::input_schema(), output_schema:json!({"type":"object","description":"Compatible workbench.learning-report.v1 evidence contract; caller-supplied observations do not prove task quality, source authenticity or savings."}), examples:vec![], - source_ref:"/srv/airouter/crates/airouter-learning/src/report.rs".into(), documentation:"/srv/airouter/crates/airouter-learning/README.md".into(), + source_ref:"/srv/airouter/crates/airouter-learning/src/report.rs".into(), documentation:"/usr/share/airouter/docs/airouter-learning/README.md".into(), }, Definition { id:"cli.airouter-learning".into(),version:airouter_learning::VERSION.into(),kind:Kind::Workflow, @@ -18,7 +18,7 @@ tags:vec!["learning".into(),"shared".into(),"cli".into()], input_schema:json!({"type":"object","properties":{"action":{"enum":["report","analysis-prepare","analysis-validate"]},"input":{"type":"string","description":"Caller-admitted readable input file."},"response":{"type":"string","description":"Required response file for analysis-validate."}},"required":["action","input"],"additionalProperties":false}), output_schema:json!({"type":"object","description":"Bounded compatible report, prepared advisory prompt/schema or validated advisory labels; errors exit nonzero."}),examples:vec![json!({"command":["airouter-learning","report","OBSERVATIONS.json"]})], - source_ref:"/srv/airouter/crates/airouter-learning/src/main.rs".into(),documentation:"/srv/airouter/crates/airouter-learning/README.md".into(), + source_ref:"/srv/airouter/crates/airouter-learning/src/main.rs".into(),documentation:"/usr/share/airouter/docs/airouter-learning/README.md".into(), }, ]; Snapshot { --- a/crates/airouter-catalog/src/bin/release.rs +++ b/crates/airouter-catalog/src/bin/release.rs @@ -21,7 +21,7 @@ output_schema: spec["outputSchema"].clone(), examples: vec![], source_ref: "/srv/airouter/crates/airouter-release/src/lib.rs".into(), - documentation: "/srv/airouter/crates/airouter-release/README.md".into(), + documentation: "/usr/share/airouter/docs/airouter-release/README.md".into(), }; let source_revision = format!( "{:x}", --- a/crates/airouter-catalog/src/bin/skills.rs +++ b/crates/airouter-catalog/src/bin/skills.rs @@ -27,7 +27,7 @@ documentation: format!("{}/SKILL.md", skill.path), }); } - capabilities.push(Definition{id:"cli.airouter-skills".into(),version:airouter_skills::VERSION.into(),kind:Kind::Workflow,title:"airouter-skills".into(),description:"Index maintained skill packages and whole-package content hashes. Tolerant list and strict replacement-catalog audit; no model calls, database or package script execution.".into(),executor:"caller-process".into(),tags:vec!["skills".into(),"catalog".into(),"rust".into(),"cli".into()],input_schema:json!({"type":"object","properties":{"action":{"enum":["list","audit"]},"root":{"type":"string"}},"required":["action","root"],"additionalProperties":false}),output_schema:json!({"type":"object","description":"SkillsList metadata and package hashes. Audit exits nonzero on incomplete discovery."}),examples:vec![],source_ref:"/srv/airouter/crates/airouter-skills/src/lib.rs".into(),documentation:"/srv/airouter/crates/airouter-skills/README.md".into()}); + capabilities.push(Definition{id:"cli.airouter-skills".into(),version:airouter_skills::VERSION.into(),kind:Kind::Workflow,title:"airouter-skills".into(),description:"Index maintained skill packages and whole-package content hashes. Tolerant list and strict replacement-catalog audit; no model calls, database or package script execution.".into(),executor:"caller-process".into(),tags:vec!["skills".into(),"catalog".into(),"rust".into(),"cli".into()],input_schema:json!({"type":"object","properties":{"action":{"enum":["list","audit"]},"root":{"type":"string"}},"required":["action","root"],"additionalProperties":false}),output_schema:json!({"type":"object","description":"SkillsList metadata and package hashes. Audit exits nonzero on incomplete discovery."}),examples:vec![],source_ref:"/srv/airouter/crates/airouter-skills/src/lib.rs".into(),documentation:"/usr/share/airouter/docs/airouter-skills/README.md".into()}); capabilities.sort_by(|a, b| a.id.cmp(&b.id)); let source_revision = format!("{:x}", Sha256::digest(serde_json::to_vec(&capabilities)?)); let snapshot = Snapshot { --- a/crates/airouter-catalog/src/bin/workspace.rs +++ b/crates/airouter-catalog/src/bin/workspace.rs @@ -22,7 +22,7 @@ input_schema: spec["inputSchema"].clone(), output_schema: json!({"type":"object","description":"MCP-shaped content/isError result. Caller binds authorized root, write scope and Unix identity; discovery grants no execution access."}), examples: vec![], source_ref: format!("/srv/airouter/crates/airouter-workspace/src/{}.rs", if name == "edit" { "exact_edit" } else { "workspace" }), - documentation: "/srv/airouter/crates/airouter-workspace/README.md".into(), + documentation: "/usr/share/airouter/docs/airouter-workspace/README.md".into(), } }).collect(), } --- /dev/null +++ b/packaging/README.md @@ -0,0 +1,85 @@ +# AIRouter package deployment + +The package contains the routing, framework and independent session daemons; +shared command, authority, coordination, workspace, browser, Codegraph, skills, +learning and verification CLIs; catalog exporters; and isolated utility service. +Install it through the distribution so its service accounts and runtime +dependencies are provisioned. Installation never enables services, creates +application credentials, enrolls providers or runs database migrations. + +The built-in HTTP session driver runs inside `airouter-sessiond`. The optional +`airouter-http-agent` subprocess example is a development/test build target, +not a separate production daemon required by this package. + +## New host + +Review `/usr/share/airouter/examples/` and the component documentation under +`/usr/share/doc/airouter-*/`. Configuration under `/etc/airouter` is protected; +examples contain placeholders, not deployment authority. Keep framework serving +disabled until its database, Unix/SQL identities, token files and exact model +profiles have been reviewed. Each independent session instance uses the +application's existing non-root Unix account and private state; it does not +inherit another application's provider credentials or tool grants. + +Router role bootstrap is `/usr/share/airouter/setup/router/bootstrap.sql`. +Framework role/bootstrap and runtime grants are in +`/usr/share/airouter/setup/framework/`. Review and run bootstrap as the database +administrator, explicitly create each dedicated database, and invoke the +embedded migrators using the migration role (`airouterctl migrate` and +`airouter-agentd --config MIGRATOR_CONFIG --migrate`). Apply framework runtime +grants after migration, including the orchestration decision journal. Serving +accounts must retain no schema CREATE, DELETE, migration or cross-service access. +These files are operator inputs; package install does not execute them. + +The provided service dependencies target PostgreSQL 18. The router can use +remote nodes and frontier providers; local Ollama is optional. Install/configure +Ollama when this host provides owned model compute. Node mTLS still requires +reviewed CA trust, certificates and the Smallstep CLI at the configured +`step_command` (the template uses `/usr/local/bin/step`). The certificate-renewal +script is an Alpha deployment template: review its CA URL and certificate paths +before provisioning certificates on another host. The package does not provision a +CA, login session, provider subscription, model weights or native vendor CLIs. + +The tools service account is `airouter-tools`, with no login and private home +`/var/lib/airouter-tools`. Configure allowed applications, resources and the +required external utilities explicitly before starting `airouter-toolsd`. +The example dependency set and Piper worker-limit patch are installed under +`/usr/share/airouter/sets/` and `/usr/share/airouter/patches/`. The set is specific +to the qualified Alpha host, including its WayVNC repository; review it before +using it elsewhere. Browser and Rust verification need the host's registered +offline toolchain/test profiles and dependency caches. Codegraph's engine is a +package dependency; application source registration and cache ownership remain +the caller's responsibility. + +Graphify's maintained launcher is `/usr/bin/airouter-graphify`; its pinned +upstream CLI is a package dependency. Configure the `AIR_GRAPHIFY_*` launcher +settings before model extraction; graph query and affected-file traversal are +deterministic. Graphify does not replace mandatory Codegraph preflight. +Existing application registrations under `/usr/local/bin/airouter-catalog`, +`/usr/local/bin/airouter-graphify` and +`/usr/local/libexec/airouter/airouter-permissions` are package-owned symlinks to +the canonical executables. During upgrade, archive any old unmanaged copies +before merging and qualify the consumer against the resolved executable bytes. + +The eleven shared skill packages and supporting assets live under +`/usr/share/airouter/skills`; the default capability refresh includes their +complete package hashes. Application policy directories may link to these +packages and retain their application-specific entries. Package installation +does not overwrite those policy directories. Stable component documentation is +under `/usr/share/airouter/docs/`, so catalog documentation works without a source +checkout. Catalog `source_ref` values retain canonical development provenance. + +## Upgrade and recovery + +Preserve existing live configuration and credential metadata. Back up persistent +state, review additive migration requirements and drain admissions before a +service restart. The session OpenRC instance can restart its dependent application +worker; account for that dependency. Package installation itself restarts nothing. +Requalify consumers after installed helper hashes change. Preserve framework +writer minimums, service journals, delivery cursors and uncertainty receipts; +never replay a task or downgrade its database to finish a package operation. + +Portage consumes regular files from its staging image during `qmerge`. Retain a +verified image snapshot outside that staging directory for rollback, together +with its exact recipe, sources and qualification receipt. A package image on +disk does not establish that a running process has adopted it. --- a/packaging/capabilities.sources.example.json +++ b/packaging/capabilities.sources.example.json @@ -56,6 +56,15 @@ "export" ], "run_as": "airouter" + }, + { + "id": "airouter-skills", + "program": "/usr/libexec/airouter/catalog/airouter-skills-catalog", + "args": [ + "export", + "/usr/share/airouter/skills" + ], + "run_as": "airouter" } ] } --- a/packaging/cron/airouter-capability-refresh +++ b/packaging/cron/airouter-capability-refresh @@ -1,3 +1,3 @@ SHELL=/bin/sh PATH=/usr/sbin:/usr/bin:/sbin:/bin -23 * * * * root /usr/libexec/airouter/capability-refresh 2>&1 | /usr/bin/logger -t airouter-capability-refresh +23 * * * * root /usr/libexec/airouter/airouter-capability-refresh 2>&1 | /usr/bin/logger -t airouter-capability-refresh --- a/packaging/cron/airouter-framework-backup +++ b/packaging/cron/airouter-framework-backup @@ -1,3 +1,3 @@ SHELL=/bin/sh PATH=/usr/sbin:/usr/bin:/sbin:/bin -17 4 * * * root /usr/libexec/airouter/framework-backup 2>&1 | /usr/bin/logger -t airouter-framework-backup +17 4 * * * root /usr/libexec/airouter/airouter-framework-backup 2>&1 | /usr/bin/logger -t airouter-framework-backup --- a/packaging/openrc/airouterd +++ b/packaging/openrc/airouterd @@ -12,7 +12,10 @@ # Mesh workers use Alpha's CA over the network and do not need a local # step-ca daemon. Authd is only needed by a Workbench gateway for the # transitional application-authorization path. - need net postgresql-18 ollama + need net postgresql-18 + # Local Ollama is optional: registered remote nodes and frontier providers + # also serve compute. Start it first when this host has enabled its service. + use ollama use step-ca use llmbridge-codex-runner } --- a/packaging/openrc/airouter-catalog-refresh +++ b/packaging/openrc/airouter-catalog-refresh @@ -8,7 +8,7 @@ start() { ebegin "Refreshing AIRouter capability definitions" - /usr/libexec/airouter/capability-refresh + /usr/libexec/airouter/airouter-capability-refresh eend $? }