Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
114 changes: 57 additions & 57 deletions OBSERVABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This repo has multiple binaries and runtime surfaces, but the same conventions a
- `apps/api` is one OTEL service
- `apps/desktop` is one Sentry/desktop service
- internal route groups or modules are not separate OTEL services
- internal logical breakdowns use `hyprnote.subsystem`
- internal logical breakdowns use `char.subsystem`

Current canonical subsystem values include:

Expand Down Expand Up @@ -51,7 +51,7 @@ We use three separate concepts:

Every process should set:

- `service.namespace = "hyprnote"`
- `service.namespace = "char"`
- `service.name = <logical process name>`
- `service.version`
- `deployment.environment`
Expand All @@ -78,13 +78,13 @@ For example, `edge`, `llm`, `stt`, and `subscription` inside `apps/api` are not

Use:

- `hyprnote.subsystem`
- `char.subsystem`

Examples:

- API ingress span: `hyprnote.subsystem = "edge"`
- LLM handler span: `hyprnote.subsystem = "llm"`
- STT websocket/session spans: `hyprnote.subsystem = "stt"`
- API ingress span: `char.subsystem = "edge"`
- LLM handler span: `char.subsystem = "llm"`
- STT websocket/session spans: `char.subsystem = "stt"`

Do not use a bare `service` span field for this.

Expand Down Expand Up @@ -161,7 +161,7 @@ It is not:

- generate it once at ingress if missing
- forward it unchanged when useful
- record it as `hyprnote.request.id`
- record it as `char.request.id`
- keep it semantically separate from OTEL trace context

Never do this:
Expand Down Expand Up @@ -208,11 +208,11 @@ Examples:
- `gen_ai.usage.input_tokens`
- `gen_ai.usage.output_tokens`

### Rule 2: Custom fields must use `hyprnote.*`
### Rule 2: Custom fields must use `char.*`

If OTEL does not define a field, use:

- `hyprnote.*`
- `char.*`

Do not use:

Expand Down Expand Up @@ -246,11 +246,11 @@ Use:

### Request and duration

- `hyprnote.request.id`
- `hyprnote.duration_ms`
- `hyprnote.retry.delay_ms`
- `hyprnote.timeout_s`
- `hyprnote.timeout.elapsed`
- `char.request.id`
- `char.duration_ms`
- `char.retry.delay_ms`
- `char.timeout_s`
- `char.timeout.elapsed`

### HTTP and routing

Expand All @@ -276,55 +276,55 @@ Use OTEL GenAI fields where available:
- `gen_ai.usage.input_tokens`
- `gen_ai.usage.output_tokens`

Use `hyprnote.*` for Hyprnote-specific request metadata:
Use `char.*` for Char-specific request metadata:

- `hyprnote.gen_ai.request.streaming`
- `hyprnote.gen_ai.request.message_count`
- `hyprnote.gen_ai.request.model_candidate_count`
- `hyprnote.gen_ai.request.tool_calling`
- `hyprnote.task.name`
- `char.gen_ai.request.streaming`
- `char.gen_ai.request.message_count`
- `char.gen_ai.request.model_candidate_count`
- `char.gen_ai.request.tool_calling`
- `char.task.name`

### STT and audio

Use:

- `hyprnote.stt.provider.name`
- `hyprnote.stt.routing_strategy`
- `hyprnote.stt.model`
- `hyprnote.stt.language_codes`
- `hyprnote.stt.language_code`
- `hyprnote.stt.session.id`
- `hyprnote.stt.job.id`
- `hyprnote.stt.provider_session.id`
- `hyprnote.stt.provider_session.duration_s`
- `hyprnote.stt.provider_session.expires_at`
- `hyprnote.stt.provider.error_code`
- `hyprnote.audio.sample_rate_hz`
- `hyprnote.audio.channel_count`
- `hyprnote.audio.channel_index`
- `hyprnote.audio.size_bytes`
- `hyprnote.audio.duration_s`
- `hyprnote.audio.device`
- `char.stt.provider.name`
- `char.stt.routing_strategy`
- `char.stt.model`
- `char.stt.language_codes`
- `char.stt.language_code`
- `char.stt.session.id`
- `char.stt.job.id`
- `char.stt.provider_session.id`
- `char.stt.provider_session.duration_s`
- `char.stt.provider_session.expires_at`
- `char.stt.provider.error_code`
- `char.audio.sample_rate_hz`
- `char.audio.channel_count`
- `char.audio.channel_index`
- `char.audio.size_bytes`
- `char.audio.duration_s`
- `char.audio.device`

### Vendor-specific fields

Keep vendor-specific fields namespaced:

- `hyprnote.supabase.*`
- `hyprnote.stripe.*`
- `hyprnote.connection.*`
- `hyprnote.integration.*`
- `hyprnote.bot.*`
- `char.supabase.*`
- `char.stripe.*`
- `char.connection.*`
- `char.integration.*`
- `char.bot.*`

Always prefer `service.peer.name` for the downstream system name.

### Payload and debug-only fields

If raw payload capture is necessary for debug logs, use:

- `hyprnote.payload.raw`
- `hyprnote.http.response.body`
- `hyprnote.http.body_preview`
- `char.payload.raw`
- `char.http.response.body`
- `char.http.body_preview`

Do not put large raw payloads on high-volume spans by default.

Expand All @@ -339,19 +339,19 @@ Honeycomb service views come from OTEL resource attributes, especially:
Because of that:

- `apps/api` must stay one Honeycomb service: `api`
- internal analysis should use `hyprnote.subsystem`
- internal analysis should use `char.subsystem`

### High cardinality

Honeycomb handles high-cardinality fields well. IDs are allowed when they help debugging.

Good high-cardinality examples:

- `hyprnote.request.id`
- `char.request.id`
- `enduser.id`
- `enduser.pseudo.id`
- `gen_ai.response.id`
- `hyprnote.stt.job.id`
- `char.stt.job.id`
- provider session IDs

Do not avoid useful IDs just because they are high cardinality.
Expand Down Expand Up @@ -402,11 +402,11 @@ Canonical Sentry tags include:
- `error.type`
- `gen_ai.provider.name`
- `gen_ai.request.model`
- `hyprnote.gen_ai.request.streaming`
- `hyprnote.stt.provider.name`
- `hyprnote.stt.routing_strategy`
- `hyprnote.stt.model`
- `hyprnote.stt.language_codes`
- `char.gen_ai.request.streaming`
- `char.stt.provider.name`
- `char.stt.routing_strategy`
- `char.stt.model`
- `char.stt.language_codes`

### Context naming

Expand All @@ -416,9 +416,9 @@ Canonical context names include:

- `gen_ai.request`
- `gen_ai.response`
- `hyprnote.stt.request`
- `hyprnote.enduser.claims`
- `hyprnote.session`
- `char.stt.request`
- `char.enduser.claims`
- `char.session`

### Sentry user

Expand Down Expand Up @@ -492,7 +492,7 @@ Meaning:

1. Decide whether the concept already has an OTEL semantic convention.
2. If yes, use the OTEL field name.
3. If no, add a `hyprnote.*` field.
3. If no, add a `char.*` field.
4. If the field will be recorded later on a span, declare it at span creation.
5. If the code crosses a network boundary, extract or inject W3C trace context.
6. If request correlation is needed, keep `x-request-id` separate from trace propagation.
Expand Down
4 changes: 2 additions & 2 deletions Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ tasks:
- task: cli:ui
- cp apps/cli-ui/.build/apple/Products/Release/char-cli-ui target/debug/
- cargo run -p cli --features standalone -- {{.CLI_ARGS}}
stat: btop -f hyprnote -u 500 --preset 1
stat: btop -f char -u 500 --preset 1

clean-plugins:
cmds:
Expand Down Expand Up @@ -66,7 +66,7 @@ tasks:

db:
env:
DB: /Users/yujonglee/Library/Application Support/hyprnote/db.sqlite
DB: /Users/yujonglee/Library/Application Support/char/db.sqlite
cmds:
- |
sqlite3 -json "$DB" 'SELECT store FROM main LIMIT 1;' |
Expand Down
2 changes: 1 addition & 1 deletion apps/api/fly.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
app = 'hyprnote-ai'
app = 'char-ai'
primary_region = 'sjc'
kill_signal = 'SIGTERM'
kill_timeout = 30
Expand Down
4 changes: 2 additions & 2 deletions apps/api/openapi.gen.json
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@
{
"name": "provider",
"in": "query",
"description": "STT provider. Use 'hyprnote' for automatic routing (default), or specify:\ndeepgram, soniox, assemblyai, gladia, elevenlabs, fireworks, openai, dashscope, mistral",
"description": "STT provider. Use 'char' for automatic routing (default), or specify:\ndeepgram, soniox, assemblyai, gladia, elevenlabs, fireworks, openai, dashscope, mistral",
"required": false,
"schema": {
"type": "string"
Expand Down Expand Up @@ -1008,7 +1008,7 @@
{
"name": "provider",
"in": "query",
"description": "STT provider. Use 'hyprnote' for automatic routing (default), or specify:\ndeepgram, soniox, assemblyai, gladia, elevenlabs, fireworks, openai, dashscope, mistral",
"description": "STT provider. Use 'char' for automatic routing (default), or specify:\ndeepgram, soniox, assemblyai, gladia, elevenlabs, fireworks, openai, dashscope, mistral",
"required": false,
"schema": {
"type": "string"
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub async fn sentry_and_analytics(mut request: Request, next: Next) -> Response

let mut ctx = BTreeMap::new();
ctx.insert(
"hyprnote.enduser.entitlements".into(),
"char.enduser.entitlements".into(),
sentry::protocol::Value::Array(
auth.claims
.entitlements
Expand All @@ -40,7 +40,7 @@ pub async fn sentry_and_analytics(mut request: Request, next: Next) -> Response
),
);
scope.set_context(
"hyprnote.enduser.claims",
"char.enduser.claims",
sentry::protocol::Context::Other(ctx),
);
});
Expand Down
Loading
Loading