Repository navigation
Conversation
added 8 commits
October 8, 2026 16:03
## Problem A box record names its box only by a generated identifier. An operator cannot attach a tenant, a user, or a conversation to a record. Attribution then depends on headers that the workload chooses to send, or on a side table that maps generated names to conversations (strands-agents#28). Two more defects are in the same path. The box's own records read OTEL_RESOURCE_ATTRIBUTES already, through the default detector of the OpenTelemetry SDK. That read has no validation: the SDK drops a malformed entry, and a strands.box. key that the box does not stamp gets through. A relayed agent payload does not get the attributes, so the two lanes of one run have different identities. ## Solution The run process reads OTEL_RESOURCE_ATTRIBUTES from its own environment once, and every box record and every relayed payload carries the attributes on its resource. - TelemetryConfig gets one method, with_resource_attributes. The pub use set of the telemetry crate does not change. - validate parses the text in the OpenTelemetry syntax and decodes each percent escape. It refuses a value longer than 1024 bytes, an entry with no `=`, an empty key, a repeated key, a bad percent escape, service.name, and each strands.box. key. run refuses a value that is not UTF-8. - The collector builds its resource without the environment detector. It keeps the telemetry.sdk.* keys and stamps the parsed list. - On each of the three receiver routes, the receiver removes an agent resource attribute under an operator key, and then stamps the list. An attribute on a span, a log record, or a metric stays the agent's. Interface: one new public method. A run whose OTEL_RESOURCE_ATTRIBUTES has a bad entry now stops before the workload starts; before, the SDK dropped the entry or passed it. There is no box.toml key, no CLI flag, and no change to RECORD_VERSION, LIVE_VERSION, or PROTOCOL_VERSION. A box run flag was rejected because it moves the frozen CLI. A [telemetry] field was rejected because it is fixed per box and takes a name from the target labels. ## Tests Each claim has a test beside it: - Parse, decode, and refuse: config::tests operator_resource_attributes_parse_and_percent_decode, an_unset_or_empty_value_adds_no_attribute, a_blank_segment_is_ignored, a_malformed_operator_attribute_refuses, a_repeated_operator_attribute_refuses, a_reserved_operator_attribute_refuses, an_invalid_percent_escape_refuses, an_operator_attribute_value_over_the_bound_refuses. - The box's own records carry the list, the box keys still win, and the telemetry.sdk.* keys stay: collector::tests operator_attributes_stamp_the_boxs_own_records, a_refused_operator_attribute_opens_no_collector. - Relayed payloads on all three routes: receive::tests operator_attributes_stamp_a_relayed_agent_payload (no resource in the payload), an_agent_cannot_forge_an_operator_attribute, an_agent_attribute_the_operator_did_not_set_survives. - The real binary: box_telemetry operator_resource_attributes_reach_every_record, a_reserved_operator_attribute_refuses_before_the_workload_starts, the_workload_does_not_see_operator_resource_attributes; and run::telemetry::tests::a_resource_attribute_value_that_is_not_utf8_refuses. Each new test failed before its change. The forge guard was proved: with the removal of operator keys deleted, an_agent_cannot_forge_an_operator_attribute fails; with only the logs route or only the metrics route changed to pass no list, both route tests fail on that route. Gates on macOS arm64: cargo fmt --check is clean, and cargo clippy --all-targets --all-features shows 0 warnings in crates/telemetry and crates/box. cargo test --no-fail-fast -p strands-box-telemetry -p strands-box --all-features: 1009 passed, 6 ignored (the same 6 as main), 0 skipped, 3 failed. The 3 failures are in runtime_mcp_policy_staging (catalog timeouts and the 256-page cost ratio). They also fail on main under load: interleaved runs of that suite gave 23/2 and 24/1 on this branch, and 25/0 and 23/2 on main. Linux aarch64 (a privileged container on macOS, rust 1.98.1): strands-box-telemetry 94 passed; box_telemetry 28 passed, 1 ignored, 0 skipped. Not run: Linux x86_64, the containment suites (test-integ), and the stamp memory peak at the 1024-byte bound, which is not measured.
… every relayed resource carries it
Author
|
Hi maintainers, could someone approve the pending workflow runs on this PR? As an outside contributor, CI and the PR-title check are at |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
A box record names its box only by a generated identifier, so an operator cannot attach a tenant, a user, or a conversation to the audit trail. Attribution then depends on headers that the untrusted workload chooses to send.
This change lets the operator set
OTEL_RESOURCE_ATTRIBUTESin the environment ofstrands-box run. Every box record and every relayed agent payload then carries those attributes on its resource.The box already read this variable in one place, by accident.
Resource::builder()runs the SDK environment detector. Thus the box's own records got the attributes with no validation (a malformed entry was dropped, and astrands.box.key that the box does not stamp got through), and relayed payloads did not get them. This change replaces that read with a validated read that applies to the two lanes.Load-bearing decisions:
box runflag was rejected because it moves the CLI. A[telemetry]field was rejected because it is fixed per box and takes a name from the target labels.=, an empty key, a repeated key, a bad percent escape,service.name, and eachstrands.box.key. The 1024-byte bound exists because the receiver stamps the list on each relayed resource entry. Without a bound, the operator value multiplies the stamp amplification thatcrates/telemetry/AGENTS.mdrecords.conversation.id. An attribute on a span, a log record, or a metric stays the agent's own data.Interface. This adds one public method,
TelemetryConfig::with_resource_attributes. Thepub useset does not change. A run whoseOTEL_RESOURCE_ATTRIBUTEShas a bad entry now stops; before, the SDK dropped or passed the entry. There is no newbox.tomlkey, no CLI flag, and no change toRECORD_VERSION,LIVE_VERSION, orPROTOCOL_VERSION. An operator whose shell already exportsOTEL_RESOURCE_ATTRIBUTES=service.name=...for other tools gets a refusal and must change the variable for box runs.Deferred: a way to "pull the audit stream per sandbox" (from the issue). A backend can filter on the new attribute, and each box already writes its own file.
Related Issues
Closes #28
Type of Change
New feature
Testing
The first commit message names the test for each claim. Each new test failed before its change. The forge guard and the logs and metrics routes were proved by mutation.
cargo test --no-fail-fast -p strands-box-telemetry -p strands-box --all-featureson macOS arm64: 1009 passed, 6 ignored (the same asmain), 0 skipped, 3 failed. The 3 failures are catalog timing tests inruntime_mcp_policy_staging. They also fail onmainunder load: interleaved runs of that suite gave 23/2 and 24/1 on this branch, and 25/0 and 23/2 onmain.strands-box-telemetry94 passed;box_telemetry28 passed, 1 ignored, 0 skipped.cargo fmt --checkis clean.cargo clippy --all-targets --all-featuresgives 0 warnings incrates/telemetryandcrates/box. I did not runjust clippywith-D warningsover the whole workspace.test-integ, and a measurement of the stamp memory peak at the 1024-byte bound.Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
🤖 Generated with Claude Code