use super::{DbHandle, Error};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Plugin,
Tool,
}
impl Kind {
fn as_str(self) -> &'static str {
match self {
Kind::Plugin => "plugin",
Kind::Tool => "tool",
}
}
}
const COMPARTMENT_LOCK_KEY: i64 = 0x0C0_3B_A87_AE_57_i64;
pub async fn ensure(
handle: &DbHandle,
kind: Kind,
owner: &str,
name: &str,
version: &str,
) -> Result<String, Error> {
let role = compartment_name(kind, owner, name, version);
let password = derived_password(&handle.admin_password, &role);
let password_literal = password.replace('\'', "''");
let database_identifier = format!("\"{}\"", handle.database.replace('"', "\"\""));
let mut tx = handle.pool.begin().await?;
sqlx::query("SELECT pg_advisory_xact_lock($1)")
.bind(COMPARTMENT_LOCK_KEY)
.execute(&mut *tx)
.await?;
sqlx::query(&format!(
"DO $$ BEGIN CREATE ROLE {role}; EXCEPTION WHEN duplicate_object THEN NULL; END $$;",
))
.execute(&mut *tx)
.await?;
sqlx::query(&format!(
"ALTER ROLE {role} LOGIN INHERIT NOSUPERUSER NOCREATEDB NOCREATEROLE \
PASSWORD '{password_literal}'",
))
.execute(&mut *tx)
.await?;
sqlx::query(&format!(
"CREATE SCHEMA IF NOT EXISTS {role} AUTHORIZATION {role}",
))
.execute(&mut *tx)
.await?;
sqlx::query(&format!("GRANT objectiveai_read TO {role}"))
.execute(&mut *tx)
.await?;
sqlx::query(&format!(
"ALTER ROLE {role} IN DATABASE {database_identifier} \
SET search_path = {role}, objectiveai",
))
.execute(&mut *tx)
.await?;
tx.commit().await?;
Ok(format!(
"postgres://{role}:{password}@{address}/{database}",
address = handle.address,
database = percent_encoding::utf8_percent_encode(
&handle.database,
percent_encoding::NON_ALPHANUMERIC,
),
))
}
fn compartment_name(kind: Kind, owner: &str, name: &str, version: &str) -> String {
let raw = format!("{}/{owner}/{name}/{version}", kind.as_str());
let hash = twox_hash::XxHash3_64::oneshot(raw.as_bytes());
let mut readable = format!(
"{}_{}_{}_{}",
kind.as_str(),
sanitize(owner),
sanitize(name),
sanitize(version),
);
readable.truncate(40);
format!("{readable}_{:08x}", hash as u32)
}
fn sanitize(part: &str) -> String {
part.to_ascii_lowercase()
.chars()
.map(|c| if c.is_ascii_alphanumeric() { c } else { '_' })
.collect()
}
fn derived_password(admin_password: &str, role: &str) -> String {
let input = format!("{admin_password}:{role}");
format!(
"{:032x}",
twox_hash::XxHash3_128::oneshot(input.as_bytes())
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compartment_names_are_postgres_safe_and_distinct() {
let a = compartment_name(Kind::Plugin, "testorg", "test-mcp-plugin", "1.0.0");
let b = compartment_name(Kind::Plugin, "testorg", "test_mcp_plugin", "1.0.0");
let c = compartment_name(Kind::Tool, "testorg", "test-mcp-plugin", "1.0.0");
for n in [&a, &b, &c] {
assert!(n.len() <= 63, "{n} exceeds identifier cap");
assert!(
n.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_'),
"{n} carries non-identifier chars",
);
assert!(n.starts_with("plugin_") || n.starts_with("tool_"));
}
assert_ne!(a, b);
assert_ne!(a, c);
}
#[test]
fn long_coordinates_stay_under_the_identifier_cap() {
let n = compartment_name(
Kind::Plugin,
"an-extremely-long-github-organization-name",
"a-repository-name-that-goes-on-and-on-forever",
"10.20.30-beta.4",
);
assert!(n.len() <= 63, "{n} ({}) exceeds identifier cap", n.len());
}
}