@@ -40,8 +40,8 @@ pub struct RunInput {
4040 pub backend : Option < crate :: backend:: BackendKind > ,
4141 /// CLI value of `--sidecar` (`local` | `<tcp://...|unix:///...>` | unset).
4242 pub sidecar_cli : crate :: sidecar:: SidecarCli ,
43- /// Optional operator-supplied capability token file, injected into the
44- /// agent environment at launch (bring-your-own token) .
43+ /// Optional canonical signed capability seed TOML. Run exports only its raw
44+ /// token and the original file path to the agent environment at launch.
4545 pub capability_file : Option < PathBuf > ,
4646 /// Override sandbox identity mode.
4747 pub identity_mode : Option < crate :: config:: SandboxIdentityMode > ,
@@ -837,6 +837,21 @@ mod tests {
837837 use super :: { RunIdentity , build_execution_env} ;
838838 use crate :: backend:: SandboxHandle ;
839839
840+ fn canonical_seed_toml ( raw_token : & str ) -> String {
841+ format ! (
842+ r#"raw_token = "{raw_token}"
843+ token_id = "ctok_01j0000000e008000000000001"
844+ agent_id = "agt_01j0000000e008000000000001"
845+ session_id = "sess_runtime"
846+ action_set = ["communication.external.send"]
847+ resource_scope = "*"
848+ issued_at = "2026-04-29T15:00:00Z"
849+ expiry = "2026-04-29T16:00:00Z"
850+ context_hash = "runtime-seed-canary"
851+ "#
852+ )
853+ }
854+
840855 /// Quotes a path the way the platform's VS Code shim does.
841856 fn quote_shim_path ( path : & std:: path:: Path ) -> String {
842857 let value = path. display ( ) . to_string ( ) ;
@@ -941,64 +956,81 @@ mod tests {
941956 }
942957
943958 #[ test]
944- fn capability_file_is_exported_when_file_source_is_used ( ) {
959+ fn capability_file_exports_exact_token_and_path_for_local_and_external_sidecars ( ) {
945960 let tempdir = tempfile:: tempdir ( ) . unwrap_or_else ( |e| panic ! ( "{e}" ) ) ;
946- let token_path = tempdir. path ( ) . join ( "cap.token" ) ;
947- fs:: write ( & token_path, "token" ) . unwrap_or_else ( |e| panic ! ( "{e}" ) ) ;
961+ let seed_path = tempdir. path ( ) . join ( "capability.toml" ) ;
962+ let raw_token = "v4.public.exact-runtime-token" ;
963+ let seed_document = canonical_seed_toml ( raw_token) ;
964+ fs:: write ( & seed_path, & seed_document) . unwrap_or_else ( |e| panic ! ( "{e}" ) ) ;
965+ let endpoint = SidecarEndpoint :: Tcp {
966+ addr : "127.0.0.1:8080" . parse ( ) . unwrap_or_else ( |e| panic ! ( "{e}" ) ) ,
967+ } ;
948968
949- let profile = ResolvedProfile {
950- id : "generic" . to_string ( ) ,
951- backend : crate :: backend:: BackendKind :: Bwrap ,
952- sidecar_endpoint : SidecarEndpoint :: Tcp {
953- addr : "127.0.0.1:8080" . parse ( ) . unwrap_or_else ( |e| panic ! ( "{e}" ) ) ,
954- } ,
955- sidecar_selection : crate :: sidecar:: SidecarSelection :: Local ,
956- env_passthrough : BTreeSet :: default ( ) ,
957- env_set : BTreeMap :: default ( ) ,
958- mounts : Vec :: new ( ) ,
959- seccomp_policy : None ,
960- network : NetworkPolicy {
961- enforce_network_namespace : false ,
962- fail_closed : true ,
963- } ,
964- identity_mode : SandboxIdentityMode :: SandboxUser ,
965- capability : CapabilityLeaseConfig {
966- source : CapabilitySource :: File {
967- path : token_path. clone ( ) ,
969+ for sidecar_selection in [
970+ crate :: sidecar:: SidecarSelection :: Local ,
971+ crate :: sidecar:: SidecarSelection :: Remote ( endpoint. clone ( ) ) ,
972+ ] {
973+ let profile = ResolvedProfile {
974+ id : "generic" . to_string ( ) ,
975+ backend : crate :: backend:: BackendKind :: Bwrap ,
976+ sidecar_endpoint : endpoint. clone ( ) ,
977+ sidecar_selection,
978+ env_passthrough : BTreeSet :: default ( ) ,
979+ env_set : BTreeMap :: default ( ) ,
980+ mounts : Vec :: new ( ) ,
981+ seccomp_policy : None ,
982+ network : NetworkPolicy {
983+ enforce_network_namespace : false ,
984+ fail_closed : true ,
968985 } ,
969- public_key_path : None ,
970- refresh_ratio : 0.60 ,
971- grace : Duration :: from_secs ( 30 ) ,
972- requested_actions : CapabilityLeaseConfig :: default_requested_actions ( ) ,
973- } ,
974- sidecar_local_exec : None ,
975- secret_gateway_addr : None ,
976- secret_providers : BTreeMap :: new ( ) ,
977- executable_policies : BTreeMap :: new ( ) ,
978- use_http_proxy_sidecar : false ,
979- allow_non_structural : false ,
980- ca_trust_mode : crate :: config:: CaTrustMode :: Sole ,
981- } ;
986+ identity_mode : SandboxIdentityMode :: SandboxUser ,
987+ capability : CapabilityLeaseConfig {
988+ source : CapabilitySource :: File {
989+ path : seed_path. clone ( ) ,
990+ } ,
991+ public_key_path : None ,
992+ refresh_ratio : 0.60 ,
993+ grace : Duration :: from_secs ( 30 ) ,
994+ requested_actions : CapabilityLeaseConfig :: default_requested_actions ( ) ,
995+ } ,
996+ sidecar_local_exec : None ,
997+ secret_gateway_addr : None ,
998+ secret_providers : BTreeMap :: new ( ) ,
999+ executable_policies : BTreeMap :: new ( ) ,
1000+ use_http_proxy_sidecar : false ,
1001+ allow_non_structural : false ,
1002+ ca_trust_mode : crate :: config:: CaTrustMode :: Sole ,
1003+ } ;
9821004
983- let identity = RunIdentity :: new ( crate :: identity:: test_agent_id ( ) , "generic" ) ;
984- let capability_token = crate :: capability:: read_capability_token ( & profile. capability . source )
985- . unwrap_or_else ( |e| panic ! ( "{e}" ) ) ;
1005+ let identity = RunIdentity :: new ( crate :: identity:: test_agent_id ( ) , "generic" ) ;
1006+ let capability_token =
1007+ crate :: capability:: read_capability_token ( & profile. capability . source )
1008+ . unwrap_or_else ( |e| panic ! ( "{e}" ) ) ;
9861009
987- let env = build_execution_env (
988- & profile,
989- & identity,
990- capability_token. as_deref ( ) ,
991- & profile. sidecar_endpoint ,
992- & BTreeMap :: default ( ) ,
993- ) ;
994- assert_eq ! (
995- env. get( "FIRMA_CAPABILITY_FILE" ) ,
996- Some ( & token_path. display( ) . to_string( ) )
997- ) ;
998- assert_eq ! (
999- env. get( "FIRMA_CAPABILITY_TOKEN" ) ,
1000- Some ( & "token" . to_string( ) )
1001- ) ;
1010+ let env = build_execution_env (
1011+ & profile,
1012+ & identity,
1013+ capability_token. as_deref ( ) ,
1014+ & profile. sidecar_endpoint ,
1015+ & BTreeMap :: default ( ) ,
1016+ ) ;
1017+ assert_eq ! (
1018+ env. get( "FIRMA_CAPABILITY_FILE" ) ,
1019+ Some ( & seed_path. display( ) . to_string( ) )
1020+ ) ;
1021+ assert_eq ! (
1022+ env. get( "FIRMA_CAPABILITY_TOKEN" ) . map( String :: as_str) ,
1023+ Some ( raw_token)
1024+ ) ;
1025+ assert_ne ! (
1026+ env. get( "FIRMA_CAPABILITY_TOKEN" ) . map( String :: as_str) ,
1027+ Some ( seed_document. as_str( ) )
1028+ ) ;
1029+ assert ! (
1030+ !env. get( "FIRMA_CAPABILITY_TOKEN" )
1031+ . is_some_and( |value| value. contains( "runtime-seed-canary" ) )
1032+ ) ;
1033+ }
10021034 }
10031035
10041036 #[ test]
0 commit comments