Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh

Commit

Sessions g1t starts on Claude Managed Agents reach the internet, as a CI runner does: their environment's networking was limited to g1t's hosts, and limited networking also limits web_search and web_fetch to those hosts, so on flagon-io/demo #1 the session could neither look up Cloudflare Connect's start time nor open its own deploy at demo-flagonio.g1t.page, though both tools were on. The environment is unrestricted now (the person chose this over an allowlist); the token is still swapped in only on requests to g1t's hosts. An account's environment was made once and never changed, so the config hash now covers the environment too, and a changed one is updated in place (POST /v1/environments/{id}) at the account's next session. The sessions guide says so. Checked: cargo test -p g1t-integrations (76), wasm32 check of g1t-integrations.

G1t-Session:
ses_01m4z09g22e7ysqpg2ha423cws
syntaqxcommitted Parent9319d68Browse files
2 files+26−190/2 viewed
+3−2
294294 commits yet, where there is nothing to open one against: it pushes the
295295 first version straight to the default branch.
296296 g1t's MCP server is attached too, for anything the CLI has no command
297− for. It can search and read the web (web search and web fetch), for a
298− fact the work needs from outside g1t. Both reach g1t with a token made for you
297+ for. Its sandbox reaches the internet as a CI runner does, and it can
298+ search and read the web (web search and web fetch), for a fact the work
299+ needs from outside g1t or to open its own deploy. Both reach g1t with a token made for you
299300 and that repository only, which lasts the session and is revoked when it
300301 ends. The token is kept in a vault on the Anthropic account: the MCP
301302 server's credential, and `G1T_TOKEN`, which the sandbox holds only as a
+23−17
151151 }
152152
153153 /// The environment g1t keeps on an Anthropic account: a cloud sandbox that
154−/// reaches package registries, g1t's MCP server, and g1t itself.
155−pub fn environment_body(name: &str, hosts: &[String]) -> Value {
154+/// reaches the web as a CI runner does. Limited networking also limited
155+/// web_search and web_fetch to its hosts, so a session could look nothing
156+/// up and couldn't open its own deploy on g1t.page. The token still goes to
157+/// g1t's hosts only (`token_credential`).
158+pub fn environment_body(name: &str) -> Value {
156159 json!({
157160 "name": name,
158− "config": {
159− "type": "cloud",
160− "networking": { "type": "limited", "allow_package_managers": true, "allow_mcp_servers": true, "allowed_hosts": hosts },
161− },
161+ "config": { "type": "cloud", "networking": { "type": "unrestricted" } },
162162 })
163163 }
164164
866866 let known = stored.as_ref().map(|stored| own_skills(&stored.skills)).unwrap_or_default();
867867 let (skills, ours) = self.agent_skills(key, &known).await?;
868868 let body = agent_body(model, &self.mcp_url(), &skills);
869− let hash = g1t_secrets::sha256_hex(&body.to_string());
870− let hosts = self.g1t_hosts();
869+ let environment = environment_body(&format!("g1t-{}-{}", row.workspace, row.id));
870+ // Both: a change to either reaches accounts at their next session.
871+ let hash = g1t_secrets::sha256_hex(&format!("{body}{environment}"));
871872 let (agent_id, version, environment_id) = match stored {
872873 Some(stored) if stored.config_hash == hash => return Ok(Ok((stored.agent_id, stored.agent_version, stored.environment_id))),
873− Some(stored) => match self.call_anthropic(key, Method::Post, &format!("/v1/agents/{}", stored.agent_id), Some(body)).await? {
874− Ok(agent) => (stored.agent_id, agent["version"].as_u64().unwrap_or(1) as u32, stored.environment_id),
875− Err(problem) => return Ok(Err(problem)),
876− },
874+ Some(stored) => {
875+ if let Err(problem) = self.call_anthropic(key, Method::Post, &format!("/v1/environments/{}", stored.environment_id), Some(environment)).await? {
876+ return Ok(Err(problem));
877+ }
878+ match self.call_anthropic(key, Method::Post, &format!("/v1/agents/{}", stored.agent_id), Some(body)).await? {
879+ Ok(agent) => (stored.agent_id, agent["version"].as_u64().unwrap_or(1) as u32, stored.environment_id),
880+ Err(problem) => return Ok(Err(problem)),
881+ }
882+ }
877883 None => {
878884 let environment = match self
879− .call_anthropic(key, Method::Post, "/v1/environments", Some(environment_body(&format!("g1t-{}-{}", row.workspace, row.id), &hosts)))
885+ .call_anthropic(key, Method::Post, "/v1/environments", Some(environment))
880886 .await?
881887 {
882888 Ok(environment) => environment["id"].as_str().unwrap_or_default().to_owned(),
16101616 assert!(prompt.contains("commit the first version straight to the default branch and push it there"), "an empty repository's first push is asked for");
16111617 assert!(prompt.contains("echo ses_1 > /workspace/web/.git/g1t/session"));
16121618 assert!(prompt.contains("G1t-Session: ses_1"));
1613− let environment = environment_body("g1t-acme-con_1", &hosts);
1614− assert_eq!(environment["config"]["networking"]["allowed_hosts"], json!(hosts));
1615− assert_eq!(environment["config"]["networking"]["allow_mcp_servers"], true);
1619+ // The sandbox reaches the web; the token, only g1t's hosts.
1620+ let environment = environment_body("g1t-acme-con_1");
1621+ assert_eq!(environment["config"]["networking"], json!({ "type": "unrestricted" }));
16161622 }
16171623
16181624 #[test]
17511757 let hosts = vec!["g1t.sh".to_owned(), "api.g1t.sh".to_owned(), "mcp.g1t.sh".to_owned()];
17521758 let bodies = json!({
17531759 "agent": agent_body(DEFAULT_MODEL, "https://mcp.g1t.sh", &[]),
1754− "environment": environment_body("g1t-smoke-check", &hosts),
1760+ "environment": environment_body("g1t-smoke-check"),
17551761 "vault": vault_body("smoke"),
17561762 "mcp_credential": mcp_credential("https://mcp.g1t.sh", "g1t_placeholder_not_a_token"),
17571763 "token_credential": token_credential("g1t_placeholder_not_a_token", &hosts),

Comments

Sign in to comment.