Juice.Observability.Data
1.2.0
Prefix Reserved
dotnet add package Juice.Observability.Data --version 1.2.0
NuGet\Install-Package Juice.Observability.Data -Version 1.2.0
<PackageReference Include="Juice.Observability.Data" Version="1.2.0" />
<PackageVersion Include="Juice.Observability.Data" Version="1.2.0" />
<PackageReference Include="Juice.Observability.Data" />
paket add Juice.Observability.Data --version 1.2.0
#r "nuget: Juice.Observability.Data, 1.2.0"
#:package Juice.Observability.Data@1.2.0
#addin nuget:?package=Juice.Observability.Data&version=1.2.0
#tool nuget:?package=Juice.Observability.Data&version=1.2.0
Juice Observability Platform
Turns telemetry from Juice services into a resource model, health, rules, alerts and investigation workflows.
| Path | Contents |
|---|---|
docs/ |
Architecture diagram, requirements and architecture, phased roadmap, Phase 0 decisions, deployment guide, runbook, load testing guide, MVP acceptance, Juice 10.8 telemetry baseline |
console/ |
Juice Console, the product UI (Angular 22, juice-layout): services, operations, traces, logs, metrics, topology, resources, alerts, rules. The pages are the @juice-js/observability library (console/projects); served by the API image; reads only the Juice API |
deploy/local/ |
Local single-tenant stack: OpenTelemetry Collector, ClickHouse, PostgreSQL, Keycloak, optional Grafana, with integration and load tests |
src/Juice.Observability.Data |
PostgreSQL domain model (EF Core) and migrations |
src/Juice.Observability.Discovery |
Discovery: reads Collector-stamped telemetry from ClickHouse and reconciles services, instances, environments, resources and dependencies |
src/Juice.Observability.Health |
Health evaluation: instances, services and applications per environment, and hosts, from ClickHouse evidence |
src/Juice.Observability.Rules |
Typed scheduled rules over ClickHouse templates and the alert lifecycle |
src/Juice.Observability.Webhooks |
Notification of alert transitions, heard from the Juice outbox: SSRF-safe delivery to signed webhooks, Slack, Teams and email, retries, history, silences, grouping, escalation |
src/Juice.Observability.Api |
/api/v1 (OIDC, tenant-scoped) and the background workers, as a library (AddJuiceObservability, MapJuiceObservability) |
src/Juice.Observability.Host |
The reference host (the image): the API library and the Console on one origin |
src/Juice.Observability.Agents |
Juice Agent control plane: the agent CA, enrollment and revocation, the mTLS agent listener, signed read-only commands |
src/Juice.Observability.Agent.Contracts |
What the platform and the agent agree on: the signed command envelope, the typed actions and their bounds |
src/Juice.Observability.Agent |
Juice Agent (juice-agent, Dockerfile.agent): runs on a host, connects only outbound, and runs only what its local allowlist permits |
test/Juice.Observability.Tests |
Unit tests, and integration tests against the running local stack |
Build and test
Needs the .NET 10 SDK. Integration tests need the local stack running with --profile test and are skipped otherwise.
deploy/local/scripts/init.sh && (cd deploy/local && docker compose --profile test up -d --wait)
dotnet test
deploy/local/scripts/run-api.sh # API on http://localhost:5180, migrates and provisions the tenant
(cd console && npm ci && npm start) # Console on http://localhost:4200, proxying /api to :5180
docker compose --profile platform up -d --build serves the Console and the API together on http://localhost:5180.
console/e2e/journey.mjs signs in through Keycloak and walks the investigation journey (Console requirements §21) in a
headless browser.
Build output goes to build/. Add a migration with
dotnet tool restore && dotnet ef migrations add <Name> --project src/Juice.Observability.Data --output-dir Migrations;
MigrationTests fails when the model changes without one.
Packages and CI
Besides the image, the platform ships as libraries for a host app of the customer's own:
| Package | Feed | Contents |
|---|---|---|
Juice.Observability.Api (NuGet) |
nuget.org; alpha/beta on GitHub Packages | AddJuiceObservabilityFiles(), AddJuiceObservability(), MapJuiceObservability(): the API, the workers, auth and health checks. Depends on .Data, .Discovery, .Health, .Rules, .Webhooks, also published |
@juice-js/observability (npm) |
npmjs | provideJuiceObservability(), OBSERVABILITY_ROUTES, observabilityMenus() and the page styles (usage) |
src/Juice.Observability.Host and console/src are the reference host and app: they use the packages the way a
customer would.
var builder = WebApplication.CreateBuilder(args);
builder.Configuration.AddJuiceObservabilityFiles();
builder.Services.AddJuiceObservability(builder.Configuration);
var app = builder.Build();
app.UseCorrelationId();
app.UseAuthentication();
app.UseAuthorization();
app.MapJuiceObservability();
app.Run();
Publishing (.github/workflows): the backend and the console library are versioned and released independently.
| Package | Its next version lives in | Release tag |
|---|---|---|
Juice.Observability.* |
<VersionPrefix> in Directory.Build.props |
api-<version> |
@juice-js/observability |
"version" in console/projects/juice-js/observability/package.json |
console-<version> |
| You do | You get |
|---|---|
push to main, dev/** |
build and test only (build.yml) |
push to release/** that changes src/** (or the props / nuget.config) |
build and test, then, if they pass, NuGet alphas at <VersionPrefix>-alpha.<run> on GitHub Packages (build.yml) |
push to release/** that changes console/projects/juice-js/observability/** |
build and test, then, if they pass, an npm alpha at <version>-alpha.<run> under @alpha (build.yml); run Build by hand on the branch to publish both |
create a GitHub release on main tagged api-1.2.0 |
the backend at 1.2.0 on nuget.org (release.yml) |
create a GitHub release on main tagged console-1.4.0 |
the console library at 1.4.0 on npm under latest |
the same, on release/1.x |
a patch of an older line; npm under release-1.x, never replacing latest |
the same, with a pre-release version (api-1.2.0-beta.1, console-1.4.0-rc.1) |
a beta or rc: nuget.org as a pre-release, npm under the label (beta, rc) |
Each next version is the one to release: raise it after a release so alphas sort above it. Tags have no v.
The console library calls the API over HTTP, so record which versions work together:
@juice-js/observability |
needs Juice.Observability.Api |
|---|---|
| 1.0.x | 1.0.x |
API
All routes need a bearer token from the deployment's OIDC provider, with audience juice-platform and a platform role
(viewer, operator or admin) in the roles claim. Errors are RFC 9457 problem details with correlationId and
traceId; send X-Correlation-Id to set your own. Lists take limit (1–200, default 50) and cursor (the previous
page's nextCursor).
| Route | Returns |
|---|---|
GET /api/v1/tenants |
the caller's tenant (from its platform_tenant claim) |
GET /api/v1/tenants/{tenantId}/applications |
applications with their service counts |
GET /api/v1/tenants/{tenantId}/resources?type= |
services first, then hosts, containers, pods, messaging destinations and databases |
GET /api/v1/tenants/{tenantId}/resources/{id} |
a service with its instances, or a resource (a host lists the instances on it) |
GET /api/v1/tenants/{tenantId}/topology?environment= |
services and resources with the dependencies between them, each with its evidence and confidence; with an environment, each node's health |
GET /api/v1/tenants/{tenantId}/health?subjectType=&environment=&state= |
current health with reasons, policy version, evidence window and stale |
GET /api/v1/tenants/{tenantId}/rules |
the rules, each with who owns it (managedBy) and its etag |
POST …/rules, PUT …/rules/{key} |
create or change a rule (admin); a typed template, never a query. If-Match with the etag refuses a change over someone else's. Disabling or rescoping resolves its open alerts |
GET, POST …/webhooks, PUT …/webhooks/{id} |
channels: webhook, slack, teams or email, with routing and groupWindowSeconds. Changes are admin only; no address or secret is ever returned |
GET, PUT, DELETE …/webhooks/allowlist |
private destinations channels may reach (admin to change; DELETE takes ?destination=) |
GET, POST …/silences, DELETE …/silences/{id} |
silences and maintenance windows (operator or admin to change); ?all=true includes those over |
GET, POST …/escalation-policies, PUT …/escalation-policies/{key} |
who is told as an alert stays open (admin to change) |
GET …/alerts/{alertId}/notifications |
an alert's deliveries with their attempts, escalations, active silences and incidents |
GET, POST …/incidents, GET …/incidents/{id} |
incidents with their alerts and record (operator or admin to change) |
POST …/incidents/{id}/transitions, …/notes, …/alerts, DELETE …/incidents/{id}/alerts/{alertId} |
moving an incident, noting, and its alerts; each audited |
GET /api/v1/tenants/{tenantId}/alerts?state=&rule= |
alerts, newest first |
GET /api/v1/tenants/{tenantId}/alerts/{alertId} |
an alert with its evidence, state history, the subject's health and any integration links |
GET /api/v1/tenants/{tenantId}/environments |
the environments seen in telemetry |
GET /api/v1/tenants/{tenantId}/telemetry/traces?serviceId=&environment=&operation=&status=&minDurationMs=&maxDurationMs=&instanceId=&resourceId=&dependencyId=&traceId=&spanId=&attribute=key=value&from=&to=&cursor= |
traces with a span matching every filter, by latest matching span (window ≤ 24 h); pass nextCursor as cursor, with the same window, for the next page |
GET /api/v1/tenants/{tenantId}/telemetry/traces/{traceId} |
traceId is the 32-hex id or a W3C traceparent; every span (≤ 1,000) with service id, instance, host, scope, attributes, events, links, and the service or resource a call reaches |
GET /api/v1/tenants/{tenantId}/telemetry/logs?serviceId=&instanceId=&resourceId=&traceId=&spanId=&minSeverity=&q=&from=&to=&cursor= |
log records, newest first; nextCursor pages back like traces |
GET /api/v1/tenants/{tenantId}/telemetry/operations?serviceId= |
a service's entry operations with throughput, error rate, p50/p95/p99 |
GET /api/v1/tenants/{tenantId}/telemetry/dependencies?environment=&from=&to= |
calls and error rate of the newest 200 edges in the window, for colouring the graph |
GET /api/v1/tenants/{tenantId}/telemetry/dependencies/{dependencyId} |
an edge's evidence: calls, error rate, p95, last seen, representative traces |
GET /api/v1/tenants/{tenantId}/telemetry/metrics, …/metrics/series?name=&stepSeconds=, …/service-stats?serviceId= |
metric names, one metric as a series (counter → rate), a service's requests, errors and p95; empty buckets have no point |
GET /api/v1/tenants/{tenantId}/telemetry/capacity?serviceId=&environment=&from=&to= |
what a service was measured to need per instance, for the Console's capacity calculator: CPU per request or message, idle CPU and memory, memory growth, highest load, whether the data supported separating them, and whether its outbox delivery keeps up (from Juice's create and deliver spans) |
POST /api/v1/tenants/{tenantId}/changes, GET …/changes?serviceId=&resourceId=&environment=&kind= |
report a change (operator or admin) and list them, newest first |
GET /api/v1/tenants/{tenantId}/timeline?serviceId=&environment=&from=&to= |
changes, health transitions and alert transitions in one order |
GET /api/v1/tenants/{tenantId}/alerts/{alertId}/related |
what happened around an alert, ranked with the reason for every point, labelled as correlation |
POST /api/v1/tenants/{tenantId}/load-runs, POST …/load-runs/{id}/end |
mark a load test's window (operator or admin; Idempotency-Key on start); the platform generates no load |
GET /api/v1/tenants/{tenantId}/load-runs?environment=, …/load-runs/{id}/results |
the runs; each service's load, p95, errors, thread pool queue, consumer busy and outbox backlog per time step, and where it started to struggle |
GET /api/v1/tenants/{tenantId}/search?q= |
a trace id or W3C traceparent, a span id (last 24 h) or services by name |
GET /api/v1/tenants/{tenantId}/webhooks |
webhook endpoints (destination host only, never the secret) |
GET /api/v1/tenants/{tenantId}/webhooks/{endpointId}/deliveries?status= |
delivery history, each with every attempt |
POST /api/v1/tenants/{tenantId}/alerts/{alertId}/acknowledgement |
acknowledge an open alert: operator or admin; send Idempotency-Key to retry safely |
POST …/agents/enrollment-tokens |
a one-time token to enroll one agent (admin), shown once, valid 5 minutes to 24 hours |
GET …/agents?all=, GET …/agents/{id}, POST …/agents/{id}/revocation |
enrolled agents and whether they are online; revoking (admin, with a reason) refuses the agent from its next call |
POST …/agents/{id}/commands, GET …/agents/{id}/commands, …/commands/{commandId} |
issue a read-only diagnostic (operator or admin) and read what the agent reported, refusals included |
A resource's detail also carries its health per environment and each instance's state. With
Integrations:Grafana:BaseUrl set, a service and an alert also carry a link to the optional Grafana overview; without
it, links is empty. Telemetry queries are platform-owned templates, tenant-filtered and bounded; services, instances,
resources and dependencies are named by platform id, never by telemetry names from the caller.
{tenantId} must be the caller's tenant; any other id, existing or not, gets the same 403. /health/live and
/health/ready are anonymous.
A local token, for example for victor (viewer):
source deploy/local/.env
curl -s -d grant_type=password -d client_id=juice-platform-cli -d username=victor \
-d "password=$KEYCLOAK_DEMO_PASSWORD" http://localhost:8080/realms/juice/protocol/openid-connect/token
juice-platform-cli exists only in the local realm, for development and tests.
Discovery
Every 30 s the worker reads telemetry from its checkpoint to one minute ago, in windows of at most 15 minutes, as
platform_reader. Every query filters on the Collector-stamped tenant. It reconciles each window and moves the
checkpoint in one transaction.
Each window also re-reads the 10 minutes before the checkpoint (Discovery:Overlap). Telemetry is stored by its own
time, so a row can land after its window was read: a long span is written when it ends but timed by its start, and the
Collector holds data back while ClickHouse is slow. Upserts make the re-read harmless. A dependency's span count adds
only spans after the checkpoint, unless the dependency is first seen in the overlap. Telemetry later than one minute
plus the overlap is still missed.
Services are keyed by namespace and name, across environments. Each namespace is one application.
Instances are keyed by
service.instance.id, or by host and process id (marked derived). An instance goes inactive after 15 minutes without telemetry and is deleted 30 days later.Resources:
Resource Key Host host.id, elsehost.nameContainer container.idPod k8s.pod.uidMessaging destination system + destination Database system + server + namespace Dependencies:
- A client or producer span whose child server or consumer span is in another service gives a dependency between
the two services, confidence
observed. - A span naming a destination or a database gives a dependency on that resource, confidence
inferred, since the resource itself never reports.
- A client or producer span whose child server or consumer span is in another service gives a dependency between
the two services, confidence
Renamed services map through
Discovery:Aliases.Limits. A window that fills
Discovery:RowLimitrows is split, down to 10 s. Values are cut to 256 characters and attributes to 64. Both are counted injuice.observability.discovery.truncated.
Health
Every 30 s the health worker reads a 5-minute evidence window ending 10 s ago. It evaluates:
- each active instance;
- each service in every environment it was seen in;
- each application per environment;
- each host.
It writes one current snapshot per subject, and a transition only when the state changes.
| State | Meaning |
|---|---|
Unknown |
Never seen, or not enough evidence. Never shown as healthy. |
Offline |
Seen before and silent for more than 5 minutes. |
Healthy, Degraded, Unhealthy, Critical |
Judged on fresh evidence. |
Instances. The error rate of server and consumer spans: 1 % is
Degradedand 5 % isUnhealthy, counted only once there are at least 3 requests. With less traffic, recent telemetry alone is a heartbeat.Services:
- a failed critical dependency (
Health:CriticalDependencies) makes the serviceCritical; - if half or more of its fresh instances are unhealthy, it is
Unhealthy; - any partial failure, or an elevated service error rate, makes it
Degraded.
Instances that went silent after a restart don't count.
- a failed critical dependency (
Applications. Children are weighted, optionally critical (
Health:ServiceWeights), with a minimum coverage of 50 %. Missing optional services never make an application unhealthy.Reasons are stable codes, such as
partial_instance_failureorcritical_dependency_failed:shop/billing.Policy version. Each snapshot and transition carries the policy version, a hash of the rule settings.
Replays. A transition is unique per subject, evidence window and policy version, so re-evaluating the same evidence never adds one.
ClickHouse unavailable. The pass fails and the states are kept. The API marks a state
staleonce it is older than three intervals.Freshness. A new failure is visible after about the evidence lag (10 s) plus ingestion; tested at 10–11 s with a 1 s evaluation loop. With the 30 s interval the bound is about 42 s, against the 60 s target.
juice.observability.health.evidence_agerecords it in operation.
Rules and alerts
Rules are deployment configuration. Rules:File names a JSON file (deploy/local/rules.json) that is validated and
loaded at startup:
- an invalid rule stops the host;
- each create, update or disable writes an audit record (actor
deployment-config); - the API only reads rules.
| Template | Value (fires above, except ThroughputAbsent) |
|---|---|
ErrorRate |
failed / all server and consumer spans |
LatencyP95 |
p95 duration of server and consumer spans, ms |
ThroughputAbsent |
requests in the window, for services with traffic in the hour before (fires at or below) |
DeliveryFailureRate |
increase of delivery_failure_total / delivery_attempt_total |
HostMemory |
used / total memory from host metrics |
MediatorErrors |
errors in the window: increase of juice.mediator.errors |
ConsumerFailures |
failed messages in the window: increase of juice.messaging.consumed with juice.dispatch.result = failure |
Each rule is scoped by Service and Environment (or Host). It also has a schedule (default 1 min), a window,
a threshold and a recovery threshold (hysteresis), minimum samples, ForDuration, RecoveryDuration and
Cooldown.
- Lifecycle. A breach opens a Pending alert. It becomes Firing once the breach has held for
ForDuration, and Resolved afterRecoveryDurationat or below the recovery threshold.- A Pending alert that stops breaching resolves without ever firing, so a transient breach never fires.
- Values between the two thresholds keep a Firing alert firing.
- After an alert that fired resolves, a new one can't fire until
Cooldownhas passed.
- Deduplication. There is one open alert per rule and subject, enforced by a unique index.
- No data. Too few samples, or no rows, means no data. It is recorded on the alert and holds the alert's state; it is never read as zero.
- Scheduler. It runs 4 evaluations at once, each cut off after 20 s. A rule still running when due again is
skipped, never stacked.
- Metrics:
juice.observability.rules.evaluations,.duration,.lag,.skippedandjuice.observability.alerts.transitions.
- Metrics:
- Breach-to-alert. Firing comes at most lag (10 s) plus one schedule after
ForDuration: 70 s for a 1-minute rule, against the 2-minute target. Measured in tests at 22 s with a 5 s schedule and a 10 sForDuration.
Notifications and incidents
Rules, channels, the allowlist and escalation can be changed through the API and the Console, by admins, with an audit record. The deployment's files still work: a row belongs to whichever side created it, and the runbook has the details.
A channel chooses what it takes by event, rule, environment and severity, and can group what arrives in a window into one notification. A silence or maintenance window holds notifications, not alerts, and what it held goes out when it ends if it still matters. An escalation policy tells more channels as an alert stays open, and stops when the alert resolves or, if the policy says so, when it's acknowledged. An incident groups alerts into a piece of work with its own record, and doesn't change them.
Status: see Phase 6 acceptance, including what hasn't been tried against real Slack, Teams and TLS mail servers.
Changes and correlation
A change event comes from one of three places:
- Telemetry: a new
service.versionfrom a new instance is recorded with no actor, since telemetry shows that it changed, not who changed it. A service's first version isn't a change, and old instances still reporting during a rollout don't read as going back. - A pipeline:
POST /changes(operator or admin). The actor comes from the token. Reporting the samesourceandexternalKeyagain returns the first event. - The platform: its own rules and webhooks when configuration loads them.
curl -X POST "$PLATFORM_URL/api/v1/tenants/$TENANT_ID/changes" -H "Authorization: Bearer $PLATFORM_TOKEN" -H 'Content-Type: application/json' -d '{
"kind": "deployment", "summary": "Deployed orders 1.4.2", "service": "shop/orders", "environment": "production", "toVersion": "1.4.2",
"source": "github-actions", "externalKey": "run-4211", "attributes": { "commit": "abc123", "url": "https://ci.example.com/runs/4211" }
}'
Secrets: only summaries are stored. Attributes are allowlisted (commit, branch, tag, pipeline, build, image, ticket, url, reason); anything else is dropped, name included. Text is masked for passwords, tokens, API keys, URL credentials and connection strings before it's stored. That catches common shapes, not every secret, so send a summary, not a command or a log.
Correlation: an alert's page lists what happened from 2 hours before its breach to 5 minutes after, on its subject, on what the subject directly depends on, and to its rule. Each item's score is the sum of points shown with their reasons:
| Reason | Points |
|---|---|
| Before the breach | up to 40, falling to 0 at 2 hours |
| After the breach began (so it didn't start it) | 5 |
| On the alert's own subject / its rule / a direct dependency | 30 / 20 / 15 |
| Deployment or version / configuration or a dependency's health / other | 15 / 10 / 5 |
| Same environment | 10 |
This is a reading order, not a diagnosis. Being close in time is correlation, and the API and the Console say so wherever these are shown.
Load test runs
The platform generates no load. A load test marks its window, and the Console's Load runs page reads each service's telemetry from it, compares runs, and shows where a service started to struggle, which everyday traffic can't reveal. Marking needs a token with the operator or admin role. The load testing guide explains how to scope and write tests. With k6:
import http from 'k6/http';
const runs = `${__ENV.PLATFORM_URL}/api/v1/tenants/${__ENV.TENANT_ID}/load-runs`;
const headers = { Authorization: `Bearer ${__ENV.PLATFORM_TOKEN}`, 'Content-Type': 'application/json' };
export const options = { stages: [{ duration: '5m', target: 50 }, { duration: '5m', target: 200 }] };
export function setup() {
const body = JSON.stringify({ name: 'checkout, up to 200 users', environment: 'staging' });
return http.post(runs, body, { headers }).json();
}
export default function () { http.get(__ENV.TARGET_URL); }
export function teardown(run) { http.post(`${runs}/${run.id}/end`, null, { headers }); }
A run with no end, because the test died, is read up to now. A limit is the first time step of two in a row where one of these holds:
| Limit | Condition |
|---|---|
latency |
p95 at least twice the fastest step in the quieter half of the run, and at least 50 ms more |
consumer-busy |
an instance spent at least 90 % of the time handling messages. A message's wait in its queue isn't in its span, so this limit doesn't show in latency |
queue |
at least 10 work items waiting for a thread (dotnet.thread_pool.queue.length) |
errors |
error rate at least 5 %, and 4 points above the quiet quarter's |
backlog |
outbox messages created but not delivered still growing, at least 10 and 5 % of those created. Includes rolled-back messages |
Figures come from spans, so they undercount if traces are sampled. Steps the window cuts short are left out, and a step runs from 15 s to an hour by the run's length. The broker's own queue depth and the database aren't measured.
Webhooks
When an alert fires, or resolves after firing, one delivery per matching endpoint is written in the same transaction as the alert change. A webhook can therefore never roll an alert back, and replaying an evaluation never notifies twice. The dispatcher sends every 2 s.
- Signing. HMAC-SHA256, Standard Webhooks form:
webhook-id,webhook-timestamp,webhook-signature: v1,<base64>over{id}.{timestamp}.{body}. - Destinations. HTTPS only, no redirects. DNS is checked on every connection: one internal answer blocks the destination. Private addresses need an allowlist entry. Loopback, link-local and cloud metadata are never reachable.
- Retries. Transient failures retry with exponential backoff and jitter (10 s to 10 min, 8 attempts). Other 4xx fail at once. Every attempt is recorded.
Configuration, secrets and receiver guidance are in docs/deployment.md.
Juice Agent
A Juice Agent runs on a host and answers read-only diagnostics: host.summary, disk.usage and tcp.reachability. It connects only outbound, to the platform's agent listener (Agents:Port, 8443; 5181 on the local stack), over mTLS. It enrolls once with a one-time token from an admin, sending a CSR for a key it generates and keeps, and renews its 7-day certificate itself.
Every command is signed by the platform's command key and checked again by the agent before it runs: the signature, that it names this agent and tenant, its time window and lifetime, its nonce (a replay is refused), the action and its parameter bounds, and this host's own allowlist, which wins over a valid signature. There is no action that runs a command, a script or an executable.
An admin can update an agent with agent.update to a package signed by a release key, kept offline. The host must trust that key in its own configuration, and the platform can't supply one. The agent's launcher runs the new version on trial: it keeps the version if it reaches the platform within 2 minutes, and goes back to the version before otherwise. The platform's command key and agent CA can be rotated without enrolling agents again. See Phase 7 acceptance, the runbook and deployment.
License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- EFCore.NamingConventions (>= 10.0.1)
- Juice.MediatR.Behaviors (>= 10.12.0)
- Juice.Messaging.Outbox.EF (>= 10.12.0)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.14)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 10.0.3)
NuGet packages (3)
Showing the top 3 NuGet packages that depend on Juice.Observability.Data:
| Package | Downloads |
|---|---|
|
Juice.Observability.Discovery
Juice Observability: discovery, health, rules and alerts over OpenTelemetry for Juice services. |
|
|
Juice.Observability.Api
Juice Observability platform API (/api/v1) and its discovery, health, rule and webhook workers, for an ASP.NET Core host. |
|
|
Juice.Observability.Agents
Juice Observability agent control plane: enrollment, mTLS agent identity, revocation and signed read-only commands. |
GitHub repositories
This package is not used by any popular GitHub repositories.