Repository navigation
Conversation
- POST /v1/logs is an OTLP/HTTP logs receiver for the JSON encoding, with optional gzip, behind the same token, staleness drop and play limit as /events; protobuf is refused with 415 and a hint - Claude Code's records map onto the events its hooks produce (prompt, tool start, tool success and failure, compaction, subagent finished), so one soundpack serves both; its other records are silent - Any other service's record maps by name and outcome: eventName, the event.name attribute or an identifier body is the hint, and success or an error severity picks the category; a free-text log line is skipped - duration_ms and error_type ride along in the event's attributes - The fixture is a real export from Claude Code 2.1.296, scrubbed; testdata/SOURCE.md says how it was captured and what was changed
- cli-reference: the /v1/logs receiver, the variables that point Claude Code at it, how its records and any other service's map to sounds, what telemetry lacks next to hooks, and the status codes - CLAUDE.md: the third way events arrive
tahodev
pushed a commit
to tahodev/claudio
that referenced
this pull request
Oct 20, 2026
LoadJSONSoundpackFromBytes silently dereferenced arbitrary absolute paths and any '..' segments from untrusted soundpack JSON, allowing a malicious soundpack to map every event to /etc/shadow, ~/.ssh/id_rsa, or similar. Split the loader API into untrusted (default; rejects absolute paths, '..' segments, and resolutions outside baseDir) and trusted-embedded (for go:embed platform JSONs that ship with the binary and use system paths like /System/Library/Sounds/Purr.aiff). The trusted variant skips path-syntax checks; the untrusted variant validates every mapping value through validateMappingValue. LoadJSONSoundpackFromBytes signature gains a baseDir parameter so it can resolve relative values consistently with LoadJSONSoundpack (closes review finding ctoth#15). Also bounds mappings count at MaxSoundpackMappings (10,000) before the existence-check loop runs, preventing a pathological JSON from pinning the inode cache during validation. The cap is enforced on both trust tiers — it is a DoS guard, not a trust check. internal/cli/cli.go switched its one call site from LoadJSONSoundpackFromBytes (now untrusted, needs baseDir) to the new LoadEmbeddedPlatformSoundpack so the shipped wsl.json/darwin.json/ windows.json continue loading with their absolute system paths. Existing soundpack_test.go tests that used absolute paths in mapping values were rewritten to use relative paths under temp directories, matching the new contract. Resolves review findings ctoth#15, ctoth#28, and ctoth#106.
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.
Why
The unit claudio plays is an event: a timestamped name with a few attributes. That is also what an OpenTelemetry log record is. Hooks are one way to get events and need claudio installed where the agent runs; telemetry is another and needs only a few environment variables there. Accepting OTLP logs lets claudio hear anything that already exports telemetry.
What
POST /v1/logson the existing listener is an OTLP/HTTP logs receiver for the JSON encoding (gzip accepted, protobuf refused with 415 and a hint). It sits behind the same token, staleness drop and limit of eight sounds as/events. No new dependencies.user_prompt,tool_decision,tool_result,compaction,subagent_completed,api_retries_exhausted. Its other records are silent. One session start sends dozens ofhook_registeredandplugin_loadedrecords.eventName, theevent.nameattribute or an identifier body becomes the hint;successor an error severity picks the category. A free-text log line is skipped.duration_msanderror_typeride along in the event's attributes. Nothing reads them yet.Evidence
internal/listen/testdata/SOURCE.mdsays how it was captured and what was changed. It settled things the docs do not say: the event name is the record body plus anevent.nameattribute,timeUnixNanois a string, andsuccessandduration_msare strings.claude -psession's telemetry and logged prompt, two tool starts, one tool success and one tool error as accepted, with the startup records silent and no playback error.go build ./...,go vet ./...,go test ./... -count=1pass locally on Windows.Known limits
tool_decision,compaction,subagent_completedandapi_retries_exhaustedcome from the documented attribute lists. None of those records was in the capture.