Repository navigation
chore: merge nerdsane/temper main (#513 to #521) - #8
Conversation
Resolve declared identity inputs before action validation and persistence across OData, core dispatch and composite sub-writes; reject caller overrides. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Use a deterministic verification timestamp and exercise override rejection, queueing, and durable owner binding through the PostgreSQL OData path. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
…icated-owner-binding-upstream feat(spec): bind action parameters to authenticated subjects
Extend HttpEndpoint with declared OData admission actions and request-scoped HTTP transports, preserving caller authority and IOA verification. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Preserve caller and module identity across local OData calls, and verify independent module secrets with compiled WASM regression fixtures. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Preserve passed evidence when serve reloads identical specs so cached verification cannot leave the application pending; cover changed and unverified specs with regressions. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Address the nine review findings with regression coverage and remove the two lint suppressions that broke the readability ratchet. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
feat(server): admit HTTP integrations through spec-declared actions
Move reusable application checks into the standard Temper CLI and reject incomplete cross-entity verification. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Keep partial standalone spec collections usable while deployment verification requires a complete application and complete cross-entity proof. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Remove verify-app and optional verification modes. Verify policies, compiled module references, matching CSDL and IOA entities, and complete cross-entity results by default. Keep source-collection behavior coverage in CI and assemble complete CLI test inputs. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Use a block scalar so the trailing Rust module separator is not parsed as a YAML mapping. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Validate qualified CSDL references and every loaded Cedar policy; route repository hooks through source checks with binding coverage and document validation helpers. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Preserve aliases through CSDL parsing and output, resolve qualified entity sets safely, and reject undeclared or ambiguous qualifiers with regression coverage. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
…ication feat(cli): strengthen default application verification
Qualify runtime table access, retain schema selection through server metadata and registry restoration, and scope SQLx migrations to a transaction without changing default search-path behavior. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
Provide a temporary data directory for the standard server test. Environment: Datadog workspace Co-Authored-By: Codex GPT-6 <noreply@localhost>
feat(postgres): support explicitly configured storage schemas
Start the telemetry setup in a child process pointed at a local OTLP/HTTP listener, and record the startup output and every exported span, log record and metric as a baseline. The baseline is recorded before any change to the setup, so a later change to the default export shows up as a diff of that file. Co-Authored-By: Claude Code <noreply@anthropic.com>
The tracing bridge leaves a log record's event time unset and the SDK fills in only the observed time, so records were exported with an event time of zero. A backend that dates records by their event time treats them as very old and can drop them while still answering with success. Add a log processor, ahead of the batch processor, that sets the event time to the observed time when a record has none. Records that already carry an event time are left untouched. The recorded baseline changes in the event time of each log record and in nothing else. Co-Authored-By: Claude Code <noreply@anthropic.com>
…standard variables Every server process reported the same identity: a fixed service name and a fixed short list of resource attributes, so an operator running many servers could not tell their telemetry apart. OTEL_SERVICE_NAME, when set, replaces the built-in service name. OTEL_RESOURCE_ATTRIBUTES is merged into the resource of traces, metrics and logs. What the server computes itself keeps precedence: runtime-id always, the environment and the version when their own variables are set, and the service name unless OTEL_SERVICE_NAME is set. Entries that are not key=value are ignored and reported in one startup warning that does not print them. With neither variable set the resource is unchanged. Co-Authored-By: Claude Code <noreply@anthropic.com>
With an endpoint configured, traces, metrics and logs were all exported, so a backend that accepts only some signals still received the others. OTEL_TRACES_EXPORTER, OTEL_METRICS_EXPORTER and OTEL_LOGS_EXPORTER set to none now switch that signal's exporter off. Unset or otlp exports as before. Any other value is reported in one startup warning and exported as otlp. A signal that is switched off keeps its provider and gets no exporter, so spans still carry trace context and the process logs as before. The startup log line names the signals that are exported. Co-Authored-By: Claude Code <noreply@anthropic.com>
Sampling always followed the caller: a span whose caller marked the request "not sampled" was never recorded, and there was no setting to change that. OTEL_TRACES_SAMPLER now accepts always_on, always_off, parentbased_always_on (the default, as before), parentbased_always_off, traceidratio and parentbased_traceidratio, with OTEL_TRACES_SAMPLER_ARG as the ratio. A sampler that is not supported is reported in one startup warning and the default is used; a ratio that is not a number from 0 to 1 is reported and 1 is used. The name-based filter did not read OTEL_TRACES_SAMPLER_ARG and still does not. It stays around whichever sampler is chosen, so the names it drops and the reduced-rate names behave the same under every sampler. Co-Authored-By: Claude Code <noreply@anthropic.com>
With per-layer filters, asking the log bridge "is logging enabled?" without logging anything can leave filter state on the thread, and the next span on that thread is then dropped by every output. The subscriber set up here filters globally, so it is not affected: the export and the log lines after such a probe are the same as without it. The test fails when the same layers are given per-layer filters (the first span after the probe is lost), so it guards the setup against that change. Adds log as a dev-dependency of temper-observe to make the probe. It was already in the lock file as a transitive dependency. Co-Authored-By: Claude Code <noreply@anthropic.com>
…export settings A value that is not supported in OTEL_TRACES_EXPORTER, OTEL_METRICS_EXPORTER, OTEL_LOGS_EXPORTER, OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG or OTEL_RESOURCE_ATTRIBUTES is reported exactly once, on the log output and as an exported log record, the server still starts, and the default applies. An empty value is the same as an unset variable. The credential in the OTLP headers reaches the listener on every signal and appears in no output. Co-Authored-By: Claude Code <noreply@anthropic.com>
The export test file had grown past 500 lines. Move the identity, signal switch, sampler and bad-value tests into modules of their own and share one baseline assertion. No test is added, removed or changed. Co-Authored-By: Claude Code <noreply@anthropic.com>
Add OTEL_SERVICE_NAME, OTEL_RESOURCE_ATTRIBUTES, the three OTEL_*_EXPORTER variables, OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG to the module documentation and to the environment variable appendix of the guide, with their defaults, the precedence of computed resource attributes, and what happens to a value that is not supported. Co-Authored-By: Claude Code <noreply@anthropic.com>
feat(observe): export settings from the standard OpenTelemetry variables, and an event time on log records
Add seed_platform_secrets_from_environment to the server's secrets module. Given the environment as name and value pairs, it caches every variable named TEMPER_SECRET_<NAME> with a non-empty value as the platform secret <name> in lower case, so TEMPER_SECRET_BUILD_TOKEN supplies build_token to every tenant. <NAME> is A-Z, 0-9 and _, starting with a letter, so no two variables can supply the same secret. A variable that does not fit, a value that is not UTF-8, a name the server already holds a platform secret for, and a variable beyond the platform budget are skipped and logged once by variable name. Seeded secrets are logged as a count. Values are never logged or returned. Nothing is logged when no prefixed variable is set. The function reads no environment and writes nothing to storage. Reading a seeded secret is authorized as before: the tests go through the resolver a module's get_secret call uses, with Cedar policies that permit and refuse it. Co-Authored-By: Claude Code <noreply@anthropic.com>
temper serve seeded two fixed variables into the secrets cache at start, ANTHROPIC_API_KEY and EXA_API_KEY, and had no way to supply any other secret with the server's configuration: it had to be stored through the secrets API once the server answered, and again after every restart of a server that keeps its cache in memory only. Seed every TEMPER_SECRET_<NAME> variable after the secrets the server sets itself, so those keep their values and an existing deployment sees no change. With no such variable set, start-up does and logs nothing new. The test starts the built server as a process, with and without prefixed variables, and checks the count, one warning per skipped variable, that the fixed variable wins over its prefixed form, that no value appears in the output, and that the server listens. Co-Authored-By: Claude Code <noreply@anthropic.com>
… precedence Add the prefix to the environment variable appendix of the guide, with the naming rule, the reach across tenants, the unchanged authorization, the precedence against a tenant's stored secret and the secrets the server sets itself, what start-up logs, and the budget. Co-Authored-By: Claude Code <noreply@anthropic.com>
The test file was 525 lines, over the 500-line rule. Move it to a tests directory under the module: shared helpers, and one file each for authorization, naming, precedence and reporting. No test is changed. Co-Authored-By: Claude Code <noreply@anthropic.com>
The secrets API refuses a value over 8192 bytes. A prefixed variable was cached whatever its size, so the limit depended on how a secret arrived. Skip a larger value and log the variable by name, like the other skipped variables. A value of exactly the limit is seeded. Co-Authored-By: Claude Code <noreply@anthropic.com>
The guide said authorization is unchanged, next to an example of a
{secret:<name>} template. That holds for a module's get_secret call, which
needs a policy that permits access_secret. A template in an integration
config is resolved when the integration runs without that check, as it is for
every secret. Since a seeded secret reaches every tenant, say so, and say to
supply this way only what every tenant's specs may use. Add the size limit to
the guide, and a test that states the template behaviour for a seeded secret.
Co-Authored-By: Claude Code <noreply@anthropic.com>
feat(secrets): seed secrets from TEMPER_SECRET_ environment variables when temper serve starts
Brings in nerdsane nerdsane#513, nerdsane#514, nerdsane#515, nerdsane#517, nerdsane#520 and nerdsane#521 (1be4b7b).
| std::thread::spawn(move || { | ||
| for stream in socket.incoming().flatten() { | ||
| let sink = Arc::clone(&sink); | ||
| std::thread::spawn(move || serve_connection(stream, &sink)); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Listener::start moves the socket into a detached thread that waits forever in incoming(). Each run_scenario creates another listener, but dropping Listener stops none of them. The sampler test alone leaves nine threads and listening sockets alive until the process exits. Keep a shutdown handle and join the thread when the listener is dropped.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/temper-observe/tests/otel_export/listener.rs
Line: 39-44
Comment:
**Test listeners never stop**
`Listener::start` moves the socket into a detached thread that waits forever in `incoming()`. Each `run_scenario` creates another listener, but dropping `Listener` stops none of them. The sampler test alone leaves nine threads and listening sockets alive until the process exits. Keep a shutdown handle and join the thread when the listener is dropped.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.| anyhow::ensure!( | ||
| specs | ||
| .join("modules") | ||
| .join(format!("{module}.wasm")) | ||
| .is_file(), | ||
| "missing compiled WASM module {module}" | ||
| ); |
There was a problem hiding this comment.
Unusable modules pass verification
The new compiled-module check only calls is_file(). An empty file or a text file named probe.wasm satisfies it, so an otherwise valid application can pass temper verify with an unusable module. Read and validate the referenced WASM bytes, and add a malformed-module regression test so authors catch this during verification rather than later.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/temper-cli/src/verify/package.rs
Line: 37-43
Comment:
**Unusable modules pass verification**
The new compiled-module check only calls `is_file()`. An empty file or a text file named `probe.wasm` satisfies it, so an otherwise valid application can pass `temper verify` with an unusable module. Read and validate the referenced WASM bytes, and add a malformed-module regression test so authors catch this during verification rather than later.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.|
CI run 37395914236 (the fork's first): every job passes except two. Both failures already exist on arni-labs
Everything else passed: compile and lint, all four DST/platform suites, spec verification L0 to L3, instrumentation hygiene, and the other 3102 workspace tests. Locally, both of these pass because that clone also has the nerdsane branches. |
Merges nerdsane/temper
main(1be4b7b) into arni-labs/tempermain(37b3437). The two split after nerdsane nerdsane#509 (029f53e); this brings in everything nerdsane merged since:sourceon a parameter)verifychecks complete application packages by default; CSDL schema aliases resolveOTEL_*variables; exported log records get an event timetemper serveseeds secrets fromTEMPER_SECRET_*variablesThe arni-labs fixes (#1 to #7) are kept as they are. Merge it with a merge commit (not squash) so the next sync from nerdsane starts from this point.
Overlap
The merge had no conflicts. Only three files changed on both sides, in separate places:
temper-jit/src/table/builder.rs: arni-labs added spawncopy, nerdsane addedparam_sources.temper-server/src/registry_bootstrap.rs: arni-labs converts old-syntax stored specs on the platform-store path, nerdsane qualifies the Postgres restore queries with the configured schema.Cargo.lock:cedar-policy-coreadded totemper-authz.Authenticated parameter binding (nerdsane#513) only applies to actions whose spec declares a parameter
source. Existing arni-labs specs declare none, so it does not change spawn copying or current apps.Verification (local, merge commit 15ebecb)
This is the first PR on the fork to get a GitHub Actions run; these also ran locally with the CI commands:
cargo fmt --check: passcargo clippy --workspace --all-targets -- -D warnings: passcargo nextest run --workspace -E 'not test(dst_)': 3554 passed, 65 skipped (#[ignore])spec_validate_endpoint,observe::,api::repl::): passcargo test --doc --workspace: passcargo test --workspace): passExisting flaky test
temper-wasmhttp_stream_outbound::outbound_streaming_1mib_roundtripfails now and then: the 1 MiB echo comes back short (950272 of 1048576 bytes in one failure). This is not from the merge. It failed 3 of 12 runs on arni-labsmain(37b3437) and 2 of 8 on this merge, and neither side changedtemper-wasmsince the fork point. It blocked the first push here through the pre-pushcargo test --workspace. It may be a real early end-of-stream in outbound streaming, so it needs its own investigation.Not covered
This does not change production. TemperPaw
mainpins arni-labs/temper by revision (37b3437 in itsCargo.lock), so TemperPaw and production move only when that pin is bumped and released. Before bumping it, check that the Katagami and TemperPaw apps still passtemper verifyunder nerdsane#515's stricter defaults, and that their WASM HTTP integrations call declared actions (nerdsane#514).🤖 Generated with Claude Code
https://claude.ai/code/session_015CNkWvhUF4HU5howkdn5Ep
The PR appears safe to merge, with two non-blocking improvements to package checks and test cleanup.
Fix with agent prompt
Summary
This merge adds authenticated action parameters, declared HTTP admission actions, stricter application verification, explicit PostgreSQL schemas, OpenTelemetry export settings, and environment-seeded secrets.
rita-agaexplicitly identifiedoutbound_streaming_1mib_roundtripas a pre-existing flaky test and deferred its investigation; it is not reported here.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD Request[Authenticated HTTP request] --> Route[Declared endpoint] Route --> Admission[Declared OData actions] Admission --> Checks[Cedar, trusted parameters, and IOA checks] Checks --> Result{Actions succeed?} Result -->|No| Reject[Return rejection] Result -->|Yes| Transport{Endpoint type} Transport --> WASM[Run WASM with action responses] Transport --> Native[Call host-installed transport] WASM --> Response[HTTP response] Native --> ResponseReviews (1) · Last reviewed commit: "Merge nerdsane/temper main into arni-lab..."