From 3f921e93c406cac32f88b9fc27cfef3ebb6bfdcd Mon Sep 17 00:00:00 2001 From: rita-aga Date: Mon, 15 Jun 2026 17:37:36 -0400 Subject: [PATCH] feat(paw): wire genesis_token so Paw agents can push to Genesis Genesis (ADR-0025) requires auth on git push; Paw agents had no Genesis credential (only a github_token pattern), so publishes failed with "could not read Username". Load GENESIS_TOKEN env into the secret vault as genesis_token (mirrors github_token), expose it in the setup secrets schema, and document the Genesis push pattern in the paw-agent skill (token supplied as the Basic-auth username). The broad fleet GitToken (gt-paw-agent) is already minted + verified on Genesis; its secret is the GENESIS_TOKEN env on openpaw. Co-Authored-By: Claude Opus 4.8 (1M context) --- crates/temperpaw/src/config.rs | 6 +++++ crates/temperpaw/src/setup_api.rs | 23 +++++++++++++++++++ crates/temperpaw/src/startup.rs | 7 ++++++ .../paw/skills/temperpaw-agent/SKILL.md | 12 ++++++++++ 4 files changed, 48 insertions(+) diff --git a/crates/temperpaw/src/config.rs b/crates/temperpaw/src/config.rs index 9ad7fa2b4..0adb0cab5 100644 --- a/crates/temperpaw/src/config.rs +++ b/crates/temperpaw/src/config.rs @@ -110,6 +110,11 @@ pub struct Config { /// GitHub token for repo cloning and PR flows. pub github_token: Option, + /// Genesis (temper-git) token for pushing app bundles to the Genesis + /// registry. Genesis requires auth on push; supplied as the Basic-auth + /// username on git pushes to the Genesis host. + pub genesis_token: Option, + /// Datadog API key for monitor and events APIs. pub dd_api_key: Option, @@ -225,6 +230,7 @@ impl Config { modal_token_secret: optional_env("MODAL_TOKEN_SECRET"), modal_bridge_url: optional_env("MODAL_BRIDGE_URL"), github_token: optional_env("GITHUB_TOKEN"), + genesis_token: optional_env("GENESIS_TOKEN"), dd_api_key: optional_env("DD_API_KEY"), dd_app_key: optional_env("DD_APP_KEY"), dd_site: std::env::var("DD_SITE").unwrap_or_else(|_| "datadoghq.com".to_string()), diff --git a/crates/temperpaw/src/setup_api.rs b/crates/temperpaw/src/setup_api.rs index e777764ab..21f93cce9 100644 --- a/crates/temperpaw/src/setup_api.rs +++ b/crates/temperpaw/src/setup_api.rs @@ -81,6 +81,7 @@ fn allowed_secret_keys() -> HashSet<&'static str> { "slack_bot_token", "slack_signing_secret", "github_token", + "genesis_token", "exa_api_key", "tensorlake_api_key", "temper_api_key", @@ -254,6 +255,13 @@ fn secrets_schema() -> Vec { required: false, description: "For repo cloning and PR flows", }, + SecretSchema { + key: "genesis_token", + category: "integrations", + label: "Genesis Token", + required: false, + description: "For pushing app bundles to the Genesis registry (Basic-auth username)", + }, // DD_* keys are infrastructure config set via Railway env vars (by `temperpaw deploy`). // They don't belong in the dashboard — change them in Railway if needed. ] @@ -3242,6 +3250,21 @@ mod tests { use temper_server::secrets::SecretsVault; use temper_store_turso::TursoEventStore; + #[test] + fn genesis_token_is_a_known_integration_secret() { + // Genesis push credential for Paw agents: it must be both an allowed + // secret key and present in the setup schema (mirrors github_token), so + // `temper.get_secret("genesis_token")` resolves the seeded value. + assert!( + allowed_secret_keys().contains(&"genesis_token"), + "genesis_token must be an allowed secret key" + ); + assert!( + secrets_schema().iter().any(|s| s.key == "genesis_token"), + "genesis_token must appear in the setup secrets schema" + ); + } + #[test] fn discord_secret_update_builds_reconnect_params_when_config_is_complete() { let params = discord_connect_params_for_secret_update( diff --git a/crates/temperpaw/src/startup.rs b/crates/temperpaw/src/startup.rs index ce8848dc5..525b38646 100644 --- a/crates/temperpaw/src/startup.rs +++ b/crates/temperpaw/src/startup.rs @@ -1240,6 +1240,13 @@ pub async fn run(mut config: Config, force_soul_setup: bool) -> Result<()> { "github_token", config.github_token ); + seed_secret!( + vault, + &storage, + &tenant, + "genesis_token", + config.genesis_token + ); seed_secret!(vault, &storage, &tenant, "dd_api_key", config.dd_api_key); seed_secret!(vault, &storage, &tenant, "dd_app_key", config.dd_app_key); seed_secret!(vault, &storage, &tenant, "exa_api_key", config.exa_api_key); diff --git a/os-apps/paw-agent/agents/paw/skills/temperpaw-agent/SKILL.md b/os-apps/paw-agent/agents/paw/skills/temperpaw-agent/SKILL.md index be354e12f..07db90e68 100644 --- a/os-apps/paw-agent/agents/paw/skills/temperpaw-agent/SKILL.md +++ b/os-apps/paw-agent/agents/paw/skills/temperpaw-agent/SKILL.md @@ -302,6 +302,18 @@ sandbox.bash("cd /workspace/repo && git push -u origin fix/my-change") sandbox.bash("cd /workspace/repo && gh pr create --title 'fix' --body 'description'") ``` +### Pushing app bundles to Genesis (temper-git) +Genesis requires auth on push — an anonymous `git push` fails with +`could not read Username`. Supply the Genesis token as the Basic-auth +**username** (Genesis maps it to your principal + scopes). Use this for +publishing a new app or updating an existing one in the Genesis registry. +```python +genesis_token = temper.get_secret("genesis_token") +genesis = "genesis-production-164d.up.railway.app" # or env TEMPERPAW_GENESIS_REGISTRY_URL host +# create or update an app bundle repo, then push it: +sandbox.bash(f"cd /workspace/app && git push https://{genesis_token}@{genesis}//.git main") +``` + ### Local development loop (verify before PRing) ```python sandbox.bash("cd /workspace/repo && pip install -r requirements.txt")