Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
f2d13ba
docs: spec for Trace Commons instance enrollment, profiles, and trace…
zmanian Jun 25, 2026
2aad1dc
docs: Slice 0 plan — trace-commons-server per-user subject
zmanian Jun 25, 2026
30fde02
docs: IronClaw plans for Trace Commons slices 1-4
zmanian Jun 25, 2026
e48fade
feat(traces): trace-credential resolver (personal invite wins, instan…
zmanian Jun 25, 2026
4a1ec03
refactor(traces): single dir-parameterized policy-path site (remove r…
zmanian Jun 25, 2026
bc18370
feat(traces): instance-level enrollment write path (scope None)
zmanian Jun 25, 2026
6ca8ddd
test(traces): make instance-enrollment test hermetic (tempdir, no glo…
zmanian Jun 25, 2026
6cfb055
feat(admin): AdminScope::enroll_instance_trace_commons (admin-gated i…
zmanian Jun 25, 2026
c859e40
feat(traces): carry optional per-user subject in upload-claim request
zmanian Jun 25, 2026
5c9774e
feat(traces): thread resolver subject into submission claim context
zmanian Jun 25, 2026
bd517b9
test(traces): claim request carries per-user subject end-to-end
zmanian Jun 25, 2026
c7343f1
feat(traces): mint_account_login_link_via_sink (POST /v1/account/logi…
zmanian Jun 25, 2026
1eb9995
fix(traces): error instead of silent misroute in account_login_links_url
zmanian Jun 25, 2026
9e25d99
feat(host_runtime): add consent-gated trace_commons.account_login_lin…
zmanian Jun 25, 2026
4686437
fix(host_runtime): route trace bearer via credential injection; resto…
zmanian Jun 25, 2026
71d6155
feat(traces): fetch_account_traces_via_sink (GET /v1/account/traces, …
zmanian Jun 25, 2026
9650d6b
feat(reborn): trace_account_traces facade method + wire types
zmanian Jun 25, 2026
baab375
feat(reborn): GET /api/webchat/v2/traces/account handler + contract test
zmanian Jun 25, 2026
4bc74a6
feat(reborn-ui): render submitted Trace Commons traces in settings
zmanian Jun 25, 2026
8dc7315
chore(traces): document flush-gate limitation, hermetic account-trace…
zmanian Jun 25, 2026
65ee74d
style(traces): cargo fmt across Trace Commons slice changes
zmanian Jun 25, 2026
a4e0f1a
feat(traces): resolver-aware flush gate (instance-enrolled users can …
zmanian Jun 26, 2026
0f80145
Merge remote-tracking branch 'origin/main' into trace-commons-instanc…
zmanian Jun 26, 2026
62cacad
Merge remote-tracking branch 'origin/main' into trace-commons-instanc…
zmanian Jun 26, 2026
d4a6b3b
fix(traces): include per-user subject in upload-claim cache key
zmanian Jun 26, 2026
c6e4f43
fix(traces): address CodeRabbit review on PR #5280
zmanian Jun 27, 2026
c3c9b63
fix(ci): cover trace_commons.account_login_link + backfill trace i18n…
zmanian Jun 30, 2026
7556c04
fix(traces): address CodeRabbit review — withhold login URL, type err…
zmanian Jun 30, 2026
f35e258
fix(traces): address CodeRabbit re-review — async persist + typed ide…
zmanian Jul 1, 2026
a89fd00
fix(traces): instance-aware enrollment across trace_commons dispatch …
zmanian Jul 1, 2026
2b5fb08
test(traces): isolated dispatch-layer e2e for instance-only enrollment
zmanian Jul 1, 2026
3a91e5f
Merge remote-tracking branch 'origin/main' into trace-commons-instanc…
zmanian Jul 1, 2026
bbe5a1c
fix(traces): sanitize bearer-staging log + typed IDs on mint entry po…
zmanian Jul 1, 2026
0c8ff9c
fix(traces): sanitize persist-path logs, preserve handle-validation c…
zmanian Jul 1, 2026
bbebc70
fix(traces): typed login-link errors, per-request bearer handle, doc …
zmanian Jul 1, 2026
523a29c
refactor(traces): type profile_token/profile_set error mappers (syste…
zmanian Jul 1, 2026
eae873b
fix(traces): split enrollment precondition from backend in token mints
zmanian Jul 1, 2026
a09ebcc
fix(traces): check login-link URL precondition before minting bearer
zmanian Jul 1, 2026
20e179f
fix(traces): make upload-claim cache key match issuer payload exactly
zmanian Jul 1, 2026
1e7cc3a
fix(traces): check response-size cap before growing the buffer
zmanian Jul 1, 2026
51c545e
fix(traces): fail loud when trace policy cannot be statted
zmanian Jul 1, 2026
14621f7
fix(traces): capture traces for instance-only enrolled users
zmanian Jul 2, 2026
f34dfa7
Merge remote-tracking branch 'origin/main' into trace-commons-instanc…
zmanian Jul 2, 2026
fea9b4d
Remove accidentally committed frontend node_modules, restore .gitignore
zmanian Jul 6, 2026
d02c109
Merge remote-tracking branch 'origin/main' into trace-commons-instanc…
zmanian Jul 6, 2026
7c05439
Address PR review feedback: egress hardening, effect declaration, ins…
zmanian Jul 6, 2026
74d2667
Pin DNS resolution on the background trace submit/status/revoke lane
zmanian Jul 6, 2026
4bc4baf
Address CodeRabbit follow-up: sanitize status log, sync plan snippets
zmanian Jul 6, 2026
ee46317
Address round-2 review: opt-out precedence, salted subjects, UI branc…
zmanian Jul 6, 2026
951ee80
Update crossbeam-epoch 0.9.18 -> 0.9.20 for RUSTSEC-2026-0204
zmanian Jul 6, 2026
b2efc85
Route login-link/account-traces claim mint through the caller's sink
zmanian Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 74 additions & 0 deletions crates/ironclaw_host_runtime/src/egress/sanitize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ pub(super) fn validate_runtime_request(
request: &RuntimeHttpEgressRequest,
leak_detector: &LeakDetector,
) -> Result<(), RuntimeHttpEgressError> {
// Outbound credentials must flow through the staged credential-injection
// path (`credential_injections`), never as raw runtime/model-supplied
// headers or `user:pass@` URLs — for ALL runtimes, including FirstParty.
// `builtin.http` is FirstParty but takes model-supplied headers, so
// exempting FirstParty here would let the model smuggle Authorization /
// Cookie / x-api-key headers to allowlisted hosts. Host-minted credentials
// (e.g. the Trace Commons bearer) are injected AFTER this guard via the
// stager + `apply_credential_injections`, so they are not present here.
if let Some((_name, _)) = request
.headers
.iter()
Expand Down Expand Up @@ -206,6 +214,72 @@ pub(super) fn sanitize_runtime_response(
#[cfg(test)]
mod tests {
use super::*;
use ironclaw_host_api::{
CapabilityId, InvocationId, NetworkMethod, NetworkPolicy, ResourceScope, RuntimeKind,
UserId,
};

fn request_with_header(runtime: RuntimeKind, header: (&str, &str)) -> RuntimeHttpEgressRequest {
RuntimeHttpEgressRequest {
runtime,
scope: ResourceScope::local_default(UserId::new("user1").unwrap(), InvocationId::new())
.unwrap(),
capability_id: CapabilityId::new("builtin.http").unwrap(),
method: NetworkMethod::Get,
url: "https://api.example.test/v1/run".to_string(),
headers: vec![(header.0.to_string(), header.1.to_string())],
body: Vec::new(),
network_policy: NetworkPolicy {
allowed_targets: vec![],
deny_private_ip_ranges: true,
max_egress_bytes: Some(4096),
},
credential_injections: vec![],
response_body_limit: Some(4096),
save_body_to: None,
timeout_ms: None,
}
}

#[test]
fn validate_runtime_request_denies_sensitive_header_for_first_party_runtime() {
// Regression: FirstParty must NOT be exempt from the sensitive-header
// guard. `builtin.http` is FirstParty but takes model-supplied headers,
// so exempting it would let the model smuggle Authorization to an
// allowlisted host. Host-minted credentials flow through the staged
// credential-injection path instead, after this guard.
let detector = LeakDetector::new();
let request = request_with_header(
RuntimeKind::FirstParty,
("authorization", "Bearer attacker"),
);

let error = validate_runtime_request(&request, &detector)
.expect_err("first-party sensitive header must be denied");

assert!(matches!(
error,
RuntimeHttpEgressError::Request { ref reason, .. }
if reason == "sensitive_header_denied"
));
}

#[test]
fn validate_runtime_request_denies_manual_url_credentials_for_first_party_runtime() {
let detector = LeakDetector::new();
let mut request =
request_with_header(RuntimeKind::FirstParty, ("accept", "application/json"));
request.url = "https://user:pass@api.example.test/v1/run".to_string();

let error = validate_runtime_request(&request, &detector)
.expect_err("first-party manual url credentials must be denied");

assert!(matches!(
error,
RuntimeHttpEgressError::Request { ref reason, .. }
if reason == "manual_credentials_denied"
));
}

#[test]
fn scan_decoded_url_for_leaks_allows_unparseable_encoded_url() {
Expand Down
1 change: 1 addition & 0 deletions crates/ironclaw_host_runtime/src/first_party.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ impl FirstPartyCapabilityRequest {
filesystem: Arc::new(ironclaw_filesystem::InMemoryBackend::new()),
runtime_http_egress,
tool_call_http_egress: None,
runtime_secret_material_stager: None,
process: Arc::new(crate::LocalHostProcessPort::new()),
secret_store: None,
audit_sink: None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,7 @@ mod tests {
filesystem: Arc::new(InMemoryBackend::new()),
runtime_http_egress: None,
tool_call_http_egress: None,
runtime_secret_material_stager: None,
process: Arc::new(LocalHostProcessPort::new()),
secret_store: None,
audit_sink: None,
Expand Down
15 changes: 12 additions & 3 deletions crates/ironclaw_host_runtime/src/first_party_tools/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ pub use skill_management::{
pub use spawn_subagent::SPAWN_SUBAGENT_CAPABILITY_ID;
pub use time::TIME_CAPABILITY_ID;
pub use trace_commons::{
TRACE_COMMONS_CREDITS_CAPABILITY_ID, TRACE_COMMONS_ONBOARD_CAPABILITY_ID,
TRACE_COMMONS_PROFILE_SET_CAPABILITY_ID, TRACE_COMMONS_PROFILE_TOKEN_CAPABILITY_ID,
TRACE_COMMONS_STATUS_CAPABILITY_ID,
TRACE_COMMONS_ACCOUNT_LOGIN_LINK_CAPABILITY_ID, TRACE_COMMONS_CREDITS_CAPABILITY_ID,
TRACE_COMMONS_ONBOARD_CAPABILITY_ID, TRACE_COMMONS_PROFILE_SET_CAPABILITY_ID,
TRACE_COMMONS_PROFILE_TOKEN_CAPABILITY_ID, TRACE_COMMONS_STATUS_CAPABILITY_ID,
};
#[cfg(any(test, feature = "test-support"))]
pub use trigger_management::TriggerManagementClock;
Expand Down Expand Up @@ -181,6 +181,7 @@ pub fn builtin_first_party_package() -> Result<ExtensionPackage, ExtensionError>
trace_commons::credits_manifest()?,
trace_commons::profile_token_manifest()?,
trace_commons::profile_set_manifest()?,
trace_commons::account_login_link_manifest()?,
profile_set::manifest()?,
];
capabilities.extend(memory::manifests()?);
Expand Down Expand Up @@ -419,6 +420,10 @@ fn builtin_first_party_base_registry() -> Result<FirstPartyCapabilityRegistry, H
CapabilityId::new(TRACE_COMMONS_PROFILE_SET_CAPABILITY_ID)?,
handler.clone(),
);
registry.insert_handler(
CapabilityId::new(TRACE_COMMONS_ACCOUNT_LOGIN_LINK_CAPABILITY_ID)?,
handler.clone(),
);
registry.insert_handler(CapabilityId::new(PROFILE_SET_CAPABILITY_ID)?, handler);
skill_management::insert_handlers(&mut registry)?;
Ok(registry)
Expand Down Expand Up @@ -538,6 +543,10 @@ impl FirstPartyCapabilityHandler for BuiltinFirstPartyTools {
TRACE_COMMONS_PROFILE_SET_CAPABILITY_ID => {
(trace_commons::dispatch_profile_set(&request).await?, None)
}
TRACE_COMMONS_ACCOUNT_LOGIN_LINK_CAPABILITY_ID => (
trace_commons::dispatch_account_login_link(&request).await?,
None,
),
capability_id => {
let Some(metadata) = coding_capability_metadata(capability_id) else {
return Err(FirstPartyCapabilityError::new(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ mod tests {
filesystem: Arc::new(InMemoryBackend::new()),
runtime_http_egress: None,
tool_call_http_egress: None,
runtime_secret_material_stager: None,
process: Arc::new(LocalHostProcessPort::new()),
secret_store: None,
audit_sink: None,
Expand Down
10 changes: 10 additions & 0 deletions crates/ironclaw_host_runtime/src/first_party_tools/schemas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,16 @@ pub(crate) fn resolve_builtin_input_schema_ref(reference: &str) -> Option<Value>
},
"additionalProperties": false
}),
"schemas/builtin/trace_commons-account_login_link.input.v1.json" => json!({
"type": "object",
"properties": {
"confirmed": {
"type": "boolean",
"description": "Must be true only after the user explicitly asked to open a Trace Commons account/profile login link in this conversation (default: false)"
}
},
"additionalProperties": false
}),
"schemas/builtin/trace_commons-profile_set.input.v1.json" => json!({
"type": "object",
"properties": {
Expand Down
Loading
Loading