Phase 9 F4: namespaced tokens for multi-tenant aggregator (#104)
Build with clawstor cache / Cargo build (clawstor-cached) (push) Failing after 2s

This commit was merged in pull request #104.
This commit is contained in:
2026-07-15 07:16:57 +00:00
parent 2169e71d54
commit baefd95427
2 changed files with 168 additions and 29 deletions
+41
View File
@@ -251,8 +251,49 @@ pub struct Config {
/// Optional Bearer token required on all HTTP POST endpoints.
/// Set to a long random string, e.g. `openssl rand -hex 32`.
/// If absent, POST endpoints are unauthenticated (internal-network use only).
///
/// When set, this is treated as an **admin** token — no namespace
/// restriction. Prefer per-app tokens under `[[aggregator.tokens]]`
/// (below) for multi-tenant setups; `api_token` stays as the
/// pre-Phase-9 escape hatch for single-tenant use.
#[serde(default)]
pub api_token: Option<String>,
/// Aggregator-side auth: per-app Bearer tokens, each scoped to a
/// namespace prefix on tag names. Enables safe multi-tenant use
/// (e.g. clawmates workspace X only touches `workspace:x:*` tags).
/// Empty by default; `api_token` above still works as a wildcard
/// admin token.
#[serde(default)]
pub aggregator: Option<AggregatorConfig>,
}
/// Aggregator-side per-app auth config. See [`Config::aggregator`].
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct AggregatorConfig {
/// One entry per app that talks to the aggregator. A token with no
/// `namespace` set is an admin token (can touch any tag); a token
/// with `namespace = "foo"` may only write tags whose name starts
/// with `foo:`.
#[serde(default)]
pub tokens: Vec<TokenEntry>,
}
/// A single Bearer token binding: `token` value → optional `namespace`
/// prefix that constrains which tag names this caller may touch.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TokenEntry {
/// The Bearer value the app presents in `Authorization: Bearer …`.
/// Long random string, e.g. `openssl rand -hex 32`.
pub token: String,
/// Tag-name prefix this token is allowed to write. Enforced with
/// a mandatory `<namespace>:` separator so `workspace:42` cannot
/// silently reach `workspace:420:*`. Absent = admin (any tag).
#[serde(default)]
pub namespace: Option<String>,
/// Human note; not consumed by auth. Shown in logs / listings.
#[serde(default)]
pub description: Option<String>,
}
impl Config {