com.worxbend.wolfram
Members list
Type members
Classlikes
One event that passed validation, with everything the publisher and the response need already decided.
One event that passed validation, with everything the publisher and the response need already decided.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
Attributes
- Companion
- class
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
AdminRoutes.type
The operational surface: metrics, health, and the OpenAPI document.
The operational surface: metrics, health, and the OpenAPI document.
These are plain Vert.x routes and not Tapir endpoints, on purpose. ADR §7.1 requires /metrics and the health probes to be excluded from http.server.requests, because they are the highest-frequency requests the process serves, they are not user traffic, and leaving them in inflates request rate while dragging latency percentiles down. Mounting them outside the Tapir interpreter makes the exclusion structural: HttpMetrics never sees them, so no exclusion list exists to fall out of date. They are also absent from the OpenAPI document for the same reason — they are not part of the API's contract with its clients.
Attributes
- Companion
- object
- Supertypes
-
class Objecttrait Matchableclass Any
The OpenAPI document and the Swagger UI that renders it.
The OpenAPI document and the Swagger UI that renders it.
Generated from the endpoint values, never written by hand. A checked-in spec is a second statement of the contract, and the two disagree the first time an error output is added — silently, because nothing compiles YAML. Here the document is a projection of the same Endpoints values the server routes on, so "the docs are wrong" is not a state this service can be in.
This replaced a hand-rolled walk of the Tapir endpoint ADT. That version could describe paths, methods, parameters and status codes, but it emitted permissive placeholders where the body schemas belong, because deriving a JSON Schema from Schema[T] is the hard half and tapir-openapi-docs is the thing that does it. The placeholder version documented everything except the part a client generator needs most.
Both JSON and YAML are served. They are the same document; code generators and curl | jq want the first, humans and git diff want the second, and rendering both from one model costs one line.
Attributes
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
ApiDocs.type
Why a request was refused, in the shape a client sees.
Why a request was refused, in the shape a client sees.
One wire shape, several Scala types, and that is the point. Every case renders to the identical {"error": {…}} envelope AIP-193 specifies, so a client has one thing to parse. But Tapir's oneOf dispatches on the runtime class, so the type is what selects the status code — which means a 401 body cannot be served with a 400 and no test has to check that it wasn't. The alternative, a single type with a status field, makes that agreement a convention somebody maintains by hand.
Attributes
- Supertypes
-
class Objecttrait Matchableclass Any
- Known subtypes
-
class Forbiddenclass InvalidArgumentclass OutOfRangeclass PayloadTooLargeclass Unauthorizedclass UnavailableShow all
Translation between the service's domain values and the HTTP surface.
Translation between the service's domain values and the HTTP surface.
It lives in one object so the mapping from a rejection to a status code and a canonical status exists exactly once. The alternative — those fields on Rejection itself — would put sttp.model and Google's RPC vocabulary in the ingestion layer and make the service untestable without a web stack, for no gain.
Attributes
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
ApiModel.type
How this deployment verifies bearer tokens.
How this deployment verifies bearer tokens.
enabled defaults to true and there is no key default. A security layer whose default state is "off" is one that ships off, because nothing fails when it is: the tests pass, the smoke test passes, and the first evidence is an unauthenticated write in production. So the only way to run without authentication is to say AUTH_ENABLED=false out loud, and the only way to run with it is to supply a key — a service configured for verification but given nothing to verify against refuses to boot rather than accepting everything.
Value parameters
- algorithm
-
one of
HS256/HS384/HS512(symmetric, needssecret) orRS256/RS384/RS512(asymmetric, needspublic-key). The algorithm is pinned by configuration and the token's ownalgheader is checked against it, never trusted: accepting the header's choice is thealg: nonefamily of attacks, and the RSA-key-as-HMAC-secret confusion that follows from letting a caller switch a public key into a symmetric role. - audience
-
when set,
audmust contain it. - issuer
-
when set,
issmust equal it exactly. - leeway
-
tolerance applied to
expandnbf, for clock skew between the issuer and this host. Small on purpose: this is the window in which a revoked token still works. - requiredScope
-
when set, the
scopeclaim must contain it. A space-delimitedscopestring is the OAuth 2 convention (RFC 8693 §4.2); a JSON array is also accepted because half the issuers in the world emit one.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
Why a request was refused before it reached the ingestion service.
Why a request was refused before it reached the ingestion service.
Two cases and not one, because they mean different things to a client and map to different status codes: 401 says "I do not know who you are, try again with a credential", 403 says "I know exactly who you are and the answer is still no". Collapsing them sends a client with an expired token into a permissions investigation and a client missing a scope into a token-refresh loop, and neither ever succeeds.
Attributes
- Supertypes
-
trait Enumtrait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
- Known subtypes
-
class Unauthenticatedclass Forbidden
The response to events:batchCreate.
The response to events:batchCreate.
events holds the successes in request order, which is the AIP-233 response shape; entries is this API's addition for its non-atomic batch, and the two counts let a client branch without walking the list.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
One element's outcome inside a batch, correlated to the request by index.
One element's outcome inside a batch, correlated to the request by index.
By index and not by name because a malformed element has no name — that is what makes it malformed — and because a batch may legitimately carry two events with the same id from two different sources.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
The outcome of one batch document: one result per element, in the order they were sent.
The outcome of one batch document: one result per element, in the order they were sent.
Order is part of the contract — a client correlates by index, because CloudEvents id is only unique per source and a batch may legitimately contain two events from two sources with the same id.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
The service's belief about broker reachability, and the only state /health/ready reads.
The service's belief about broker reachability, and the only state /health/ready reads.
Why belief and not a probe per request. Readiness is checked by a load balancer several times a second. Asking Kafka each time would put a blocking metadata call on the probe path, so a broker that is slow would make readiness time out — the probe would fail for a reason unrelated to the answer it was asking for. Instead the evidence is collected where it already exists: every produce either succeeds or fails, and a periodic probe covers the idle case. The probe reads a single volatile reference.
Why the initial state is not ready. A process that has never reached the broker must not receive traffic, and "no evidence" is not "good news". Unknown therefore fails readiness, and the composition root probes once synchronously at boot so the first answer is real rather than pessimistic.
Liveness deliberately does not consult this (ADR §7 / Kubernetes practice): a broker outage must not restart every ingestion pod, which would turn a recoverable dependency failure into a crash loop that outlives it.
Attributes
- Companion
- object
- Supertypes
-
class Objecttrait Matchableclass Any
Attributes
- Companion
- class
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
BrokerHealth.type
Where an accepted event landed on the log.
Where an accepted event landed on the log.
A nested message rather than three fields flattened onto Event, per AIP-144: these describe the storage of the event, not the event, and a client that only wants the CloudEvent should not have to know which of seven sibling fields belong to it. It is also the part a future transport would replace wholesale.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
The HTTP contract, as values.
The HTTP contract, as values.
Endpoint descriptions are separated from server logic so that the same values drive four things that must not be allowed to disagree: the Vert.x routes, the OpenAPI document, the Swagger UI, and the stub-server tests. Writing the contract once is the entire argument for Tapir in this service.
Resource-oriented design
The surface follows Google's API Improvement Proposals, because they answer the questions a hand-rolled HTTP API answers differently every time — and answer them the way most tooling already expects.
- AIP-133/135 — standard methods on a collection. The resource is
events; creating one isPOST /v1/events, and the response is the created resource, not a bespoke receipt. - AIP-122 — resource names. Every response carries
name: "events/{event}". A client stores that. - AIP-136 — custom methods use
POST /v1/{collection}:verb, with a colon, not a sub-path. The colon is the whole point:POST /v1/events/batchis indistinguishable from creating a resource namedbatch, and the day aGET /v1/events/{event}is added those two routes collide.:batchCreatecan never collide with a resource id, because:is reserved from the identifier segment. - AIP-185 — the
/v1prefix is on the path and not in a header, so a proxy can route on it and a browser can be pointed at it. - AIP-193 — errors are one
{"error": {code, message, status, details}}envelope for every failure.
Both CloudEvents HTTP content modes arrive on one endpoint, and that is the spec's shape, not a shortcut. The CloudEvents HTTP binding defines binary and structured mode as two encodings of a request to the same resource; a client picks one by setting headers, and a server must accept either. Two Tapir endpoints on one path would also be fragile: routing between them would hinge on how the decode-failure handler classifies a missing header, which is behaviour of the interpreter rather than of the contract. So the request body is taken raw and the mode is decided by HttpBinding.modeOf — the same precedence rule modules/eventing uses on the Kafka side.
The body is byteArrayBody, not stringBody or jsonBody. Binary mode's payload is arbitrary bytes and must reach Kafka byte-identical (ADR §4.3); decoding it as a String would corrupt any non-UTF-8 payload, and decoding it as JSON would reject the very events binary mode exists to carry. The size ceiling is enforced on those bytes, before anything is decoded.
The consequence is that the generated document declares one request media type, application/octet-stream, while the service accepts any Content-Type — the header is an input to HttpBinding.modeOf, not a routing key. A oneOfBody per media type would document the three a client actually sends, but it would also make Tapir dispatch on Content-Type, which is the precedence rule this endpoint exists to avoid.
Attributes
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
Endpoints.type
The body of an error response (AIP-193).
The body of an error response (AIP-193).
Value parameters
- code
-
the HTTP status, repeated inside the body. Redundant over the wire and worth it: it survives proxies that rewrite status codes and logs that record only the payload.
- status
-
the canonical
google.rpc.Codename —INVALID_ARGUMENT,OUT_OF_RANGE,UNAUTHENTICATED,PERMISSION_DENIED,UNAVAILABLE. This is the field a generated client switches on; the HTTP status is coarser (three distinct causes all arrive as 400) and the message is prose.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
One machine-readable cause, in the shape of google.rpc.ErrorInfo (AIP-193).
One machine-readable cause, in the shape of google.rpc.ErrorInfo (AIP-193).
reason is the closed-set value a client may branch on, and it is deliberately the same vocabulary as the ingest.events.rejected{reason} metric tag: an operator reading a dashboard and a client reading an error are then looking at the same word for the same condition. metadata carries the numbers — the limit that was exceeded, the window that was missed — because putting them only in the prose message forces clients to parse English.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
One published event, as this API represents it.
One published event, as this API represents it.
name is the resource name and it comes first, per AIP-122. events/{event}, where {event} is the percent-encoded CloudEvents (source, id) pair — that pair and not id alone is what the specification makes unique.
The identity is echoed, never assigned. CloudEvents makes (source, id) the producer's contract and the whole downstream deduplication story — cobalt's ON CONFLICT DO NOTHING — depends on this service not touching it.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
The seam between ingestion and Kafka.
The seam between ingestion and Kafka.
A trait with one production implementation exists here for exactly one reason, and it is not "mockability in principle": every endpoint test in this service needs to drive the rejection paths — 503 on an unreachable broker, shed load under backpressure — and there is no way to make a real KafkaProducer fail on demand without a broker. The alternative is testing the HTTP contract against a container, which moves the fast tier into the slow one.
Failures are Either in the success channel of the Future, not a failed Future. A failed Future would make "the broker is down" indistinguishable at the type level from "the code threw", and the two want different HTTP statuses, different metrics and different log levels.
Attributes
- Supertypes
-
trait AutoCloseableclass Objecttrait Matchableclass Any
- Known subtypes
-
class KafkaEventPublisher
- A verified credential without the scope this operation needs.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalstrait ApiErrorclass Objecttrait Matchableclass AnyShow all
The CloudEvents HTTP protocol binding: HTTP headers plus a body, in, an Envelope out.
The CloudEvents HTTP protocol binding: HTTP headers plus a body, in, an Envelope out.
modules/eventing owns the Kafka binding, and the two are deliberately not the same code. They differ in the one place that matters — Kafka header names are ce_type, HTTP header names are ce-type, because Kafka historically disallowed - in header keys and the CloudEvents spec follows each transport's convention. Sharing an implementation across them would mean a prefix parameter threaded through every function and a class of bug (accepting ce_type over HTTP, which no conformant client sends) that no test would think to look for.
What is shared, and must be, is the decoding of the attributes themselves: this object assembles a CloudEvents JSON Format 1.0 document from the headers and hands it to modules/kernel's hand-written codec. That gives binary mode and structured mode literally the same validation, the same error sentences and the same notion of what an extension is — which is what makes "the same event sent in either mode produces the same row" true by construction rather than by a test that compares two parsers.
The cost is one round trip through Json for a binary-mode request. It is worth it: the alternative is a second attribute parser in a second module, and ADR §2 exists precisely because a second copy of the wire contract drifts.
Attributes
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
HttpBinding.type
http.server.requests, timed by Micrometer and by nothing else.
http.server.requests, timed by Micrometer and by nothing else.
ADR §7.1 makes this the enforceable rule of the whole observability design: exactly one component times each HTTP request in each of the three services, and it is always Micrometer, so the meter has identical names and tags in Play, in Cask and here — and one Grafana dashboard works for the fleet. ADR §3.6 rejects tapir-prometheus-metrics for precisely this reason: it publishes request_total / request_duration_seconds, which is a second, parallel family that no shared dashboard can join against.
uri is the matched route template, never the raw path. ADR §7.1 calls unbounded uri cardinality the single most likely way to take down Prometheus in this system. Telemetry installs a cap on this exact meter and tag as a backstop; using the template keeps the normal case bounded so the cap never has to fire.
/metrics and the health probes are absent from this family for free: they are plain Vert.x routes, not Tapir endpoints, so the interceptor never sees them. That is the ADR's requirement met by construction rather than by an exclusion list that someone has to remember to update.
Attributes
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
HttpMetrics.type
The endpoints bound to IngestionService and to the token verifier.
The endpoints bound to IngestionService and to the token verifier.
A class rather than an object because both are constructor dependencies: wolfram has no global state, and a singleton holding a Kafka producer would make the whole HTTP layer untestable and un-restartable.
Attributes
- Companion
- object
- Supertypes
-
class Objecttrait Matchableclass Any
Attributes
- Companion
- class
- Supertypes
-
trait Producttrait Mirrorclass Objecttrait Matchableclass Any
- Self type
-
IngestConfig.type
The validation limits wolfram applies before an event is allowed onto the topic.
The validation limits wolfram applies before an event is allowed onto the topic.
Every one of these is a rejection threshold, never a clamping-into-range one. ADR §4.3 is explicit — "reject; never invent defaults" — because each of these values ends up deciding which Postgres partition the row lands in or how much broker memory one request can pin, and silently repairing a producer's mistake makes the mistake permanent and invisible.
Value parameters
- maxBatchEvents
-
ceiling on the number of events in one
application/cloudevents-batch+jsondocument. A batch is published event-by-event, so an unbounded batch is an unbounded number of in-flight sends from one request. - maxEventBytes
-
ceiling on a single event's raw body. Kafka's own
message.max.byteswould reject an oversize record too, but it would do so after the API had already accepted the request, turning a 413 into a 503. - maxFutureSkew
-
how far ahead of the ingest clock a
timemay be. ADR §12.4 fixes this at 24 h: enough for the worst plausible device clock drift, short enough that a garbage timestamp cannot create a partition years ahead. - maxPastSkew
-
how far behind. ADR §12.4 fixes this at 90 d, which is the backfill window the partition-maintenance job keeps open; older than that and there is no partition to land in, so the row would go to the DEFAULT partition.
Attributes
- Companion
- object
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
Attributes
- Companion
- class
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
IngestMetrics.type
wolfram's domain metrics, expressed only in the shared vocabulary of modules/observability.
wolfram's domain metrics, expressed only in the shared vocabulary of modules/observability.
No meter name or tag value is invented here. ADR §7.1 makes that the enforceable rule: three services, one Grafana dashboard, so the names live in Meters and this class is a typed façade over them. The façade earns its keep by making the tags impossible to get wrong — ingest.events.received is tagged (type, mode) in exactly one place, so a second call site cannot register the same meter with a different tag set and split the timeseries in two (which Micrometer permits and Prometheus renders as two unrelated series).
Broker errors are not a separate meter. They are the outcome=failure count of Meters.KafkaProduceLatency, which is a Timer and therefore already publishes a count per tag combination. Adding a kafka.produce.errors counter would mean the same event incremented two meters that must then be kept consistent, and would put a name in wolfram's exposition that cobalt's dashboard does not know.
-Werror trap (ADR §7.4). Counter#increment and Timer#record return Unit, but MeterRegistry#counter returns the meter, so a bare registry.counter(...) line would trip -Wnonunit-statement. Every builder call below is therefore part of an expression.
Attributes
- Companion
- object
- Supertypes
-
class Objecttrait Matchableclass Any
The application service: decode, validate, publish. wolfram's whole product.
The application service: decode, validate, publish. wolfram's whole product.
Layering. This class knows nothing about HTTP — no status codes, no media-type constants beyond the CloudEvents binding's own, no Tapir. That is what lets the endpoint tests drive the interesting cases (every rejection path, batch partial failure) through a stub broker, and lets this class be tested without a server at all. The mapping from Rejection to a status code lives once, in ApiModel.
Order of checks is deliberate. Size first, because a 2 GB body must be refused before it is turned into a String; then decode, because everything after needs an envelope; then the time clamp, because it is the only check whose failure is a policy rather than a spec violation and it reads better in a message when the event is otherwise known-good.
The partition key is not computed here. It is read off the envelope — Envelope.partitionKey, kernel's single definition (ADR §4.1). This service holds it only so the response can report it and so a test can assert the delegation; recomputing source#subject locally is the exact drift modules/kernel exists to prevent, and it would be invisible until someone plotted a device timeline months later.
Attributes
- Supertypes
-
class Objecttrait Matchableclass Any
- The request is not a CloudEvent this service can accept.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalstrait ApiErrorclass Objecttrait Matchableclass AnyShow all
Attributes
- Companion
- class
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
JwtVerifier.type
Verifies bearer tokens, or verifies nothing and says so.
Verifies bearer tokens, or verifies nothing and says so.
Constructed through JwtVerifier.from, which returns an Either. Every way a verifier can be misconfigured — an unknown algorithm, an HMAC algorithm with no secret, an RSA algorithm with an unparseable key — is a boot failure with a sentence naming the field, not a runtime 500 on the first authenticated request. That is the same discipline the rest of this service applies to its Kafka and time configuration.
Attributes
- Companion
- object
- Supertypes
-
class Objecttrait Matchableclass Any
Publishes validated envelopes to events.cloudevents.v1 with a plain KafkaProducer.
Publishes validated envelopes to events.cloudevents.v1 with a plain KafkaProducer.
No Pekko here, by ADR §1. wolfram is a request/response service: every produce belongs to exactly one in-flight HTTP request, so there is no stream to backpressure and nothing for a materializer to do. A Producer plus a Promise is the whole of it, and it keeps Pekko off this service's classpath entirely.
send is not as asynchronous as it looks, and this class is built around that. KafkaProducer.send blocks the calling thread while topic metadata is unknown or the record accumulator is full, for up to max.block.ms. On a Vert.x event loop that is not a latency problem, it is an availability one — the loop serves every other connection too. So sends are handed to a single-threaded executor with a bounded queue:
- single-threaded because the accumulator is what actually batches, and one thread calling
sendin order keeps the per-key ordering thatEnvelope.partitionKeyexists to provide. A pool would let two records for the same device race into the accumulator in the wrong order. - bounded because that is the backpressure. When the queue is full the executor's abort policy throws, the request is refused with a 503, and the client backs off. The alternative — an unbounded queue — converts broker unavailability into heap exhaustion, and turns a shed request into an OOM kill several minutes later.
Trace context is captured, never inherited. Context.current() is read on the calling thread, at the moment publish is invoked, and passed explicitly across the hand-off. ADR §7.2 names this as the standing hazard: OTel's context is ThreadLocal-backed, so reading it on the sender thread would silently return root and orphan every produce span from the ingest span that caused it.
The span ends in the broker callback, not when send returns, so kafka.produce.latency and the span duration measure the same thing: acknowledgement, which is what acks=all makes meaningful.
Attributes
- Companion
- object
- Supertypes
-
trait StrictLoggingtrait EventPublishertrait AutoCloseableclass Objecttrait Matchableclass AnyShow all
Attributes
- Companion
- class
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
KafkaEventPublisher.type
The process entry point.
The process entry point.
Attributes
- Supertypes
-
trait StrictLoggingclass Objecttrait Matchableclass Any
- Self type
-
Main.type
400, distinct in the canonical vocabulary: well-formed, but time is outside the plausibility window.
400, distinct in the canonical vocabulary: well-formed, but time is outside the plausibility window.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalstrait ApiErrorclass Objecttrait Matchableclass AnyShow all
- The body, or the number of events in a batch, exceeded a configured ceiling.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalstrait ApiErrorclass Objecttrait Matchableclass AnyShow all
Who made the request, once the token has been verified.
Who made the request, once the token has been verified.
Deliberately small. Everything on it was proved by the signature check — the subject, the scopes and the issuer are copied out of a claim set that the verifier has already refused to return if the signature, exp, nbf, iss or aud were wrong. Nothing downstream re-checks, and nothing downstream can, because the raw token does not travel past JwtVerifier: a Principal in scope is the authorisation decision.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
What the broker said when it accepted a record.
What the broker said when it accepted a record.
Returned to the client because the triple (topic, partition, offset) is the only receipt that lets an operator find the event again with kcat when someone asks "did my event arrive". key is included because it is the partition key kernel computed, and a producer that expected its events on one partition can see immediately that they are not.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
Attributes
- Companion
- class
- Supertypes
-
trait Producttrait Mirrorclass Objecttrait Matchableclass Any
- Self type
-
PublisherConfig.type
Everything the Kafka producer needs, with the blocking behaviour named rather than left to Kafka's defaults.
Everything the Kafka producer needs, with the blocking behaviour named rather than left to Kafka's defaults.
KafkaProducer.send is only mostly asynchronous: it blocks the calling thread while topic metadata is unknown or the accumulator is full, for up to max.block.ms — whose default is 60 s. On an event loop that is an outage, so this build sets it low and deliberately, and routes sends through a bounded queue (see com.worxbend.wolfram.KafkaEventPublisher) so that saturation surfaces as a 503 instead of as a growing pile of parked event-loop threads.
Value parameters
- properties
-
free-form producer overrides (security, DNS, tuning). Merged under
KafkaCodecs.producerDefaults, so a deployment cannot switch idempotence oracks=alloff from a config file. - queueCapacity
-
depth of the publisher's hand-off queue. This is the service's explicit backpressure setting: when it is full, ingestion sheds load rather than buffering unboundedly in front of a broker that is not keeping up.
Attributes
- Companion
- object
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
A published event and the broker's receipt for it.
A published event and the broker's receipt for it.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
Why an event was not accepted — a value, never an exception.
Why an event was not accepted — a value, never an exception.
The reason this is an ADT and not a thrown IllegalArgumentException is that every rejection has to be three things at once and all three have to agree: an HTTP status, a metric tag, and a sentence a client can act on. An exception carries only the sentence, so the status and the tag get re-derived at the catch site — and drift the moment a fourth rejection kind is added. Here the three live on one case and the compiler checks the match.
reason is drawn from Meters.Reasons and nothing else. That closed set is the shared vocabulary of ADR §7.1 and it deliberately has fewer values than this enum has cases: Malformed and ImplausibleTime are different sentences for a client but the same actionable category for an operator, and minting a per-case tag value would fork the ingest.events.rejected dashboard away from cobalt's consume.records.poison.
BrokerUnavailable is tagged Meters.Reasons.Unpersistable for the same reason: it is the closest member of the closed set (the event could not be made durable), and adding a broker-unavailable value here would mean adding it to modules/observability, i.e. changing the vocabulary of all three services to describe a condition only one of them can be in. Its rate is separately visible as the outcome=failure count of kafka.produce.latency.
Attributes
- Supertypes
-
trait Enumtrait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
- Known subtypes
-
class Malformedclass InvalidAttributesclass ImplausibleTimeclass TooLargeclass BrokerUnavailable
Where the HTTP listener binds.
Where the HTTP listener binds.
Separate from the ingestion and publisher settings because it is the one part of the configuration a platform team owns rather than the service team: container orchestrators inject the port, nobody tunes the time clamp from a sidecar.
Attributes
- Companion
- object
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
Attributes
- Companion
- class
- Supertypes
-
trait Producttrait Mirrorclass Objecttrait Matchableclass Any
- Self type
-
ServerConfig.type
The ingest-time plausibility check on CloudEvents time (ADR §4.3, §12.4).
The ingest-time plausibility check on CloudEvents time (ADR §4.3, §12.4).
Why this exists at the edge and nowhere else. events.cloud_event is RANGE-partitioned on occurred_at, which is the event's own time. A device whose clock reads 2038 does not produce a wrong-looking row, it produces a row in a partition that does not exist — so it lands in the DEFAULT partition, where it is invisible to partition pruning. ADR §12.4 spells out the consequence: once the default partition holds rows, creating an overlapping partition takes ACCESS EXCLUSIVE and scans it. That is a maintenance outage caused by one bad clock, and the only cheap place to prevent it is the one process that sees the event before it is durable.
Why it rejects rather than clamps, despite the name. Rewriting an implausible time to now would produce a row that is silently wrong — indistinguishable from a correctly-timestamped event, and impossible to repair later because the original value is gone. ADR §4.3 says it in four words: "reject; never invent defaults". The name "clamp" is the ADR's and is kept so the two documents talk about the same thing.
Why an absent time is also a rejection. modules/kernel models time as Option on purpose: the CloudEvents spec makes it OPTIONAL and the codec must stay total, or a DLQ record could not be parsed back. The ingestion API is allowed to be stricter than the codec, and it has to be: occurred_at is NOT NULL in the DDL, so an event with no time has no row it could ever become. Rejecting at the edge with a sentence naming the missing attribute is strictly better than accepting it and having cobalt dead-letter it minutes later.
The window is asymmetric — hours ahead, months behind — because the two directions fail differently. A future timestamp is almost always a broken clock and creating partitions ahead of real time is expensive; a past timestamp is usually a gateway draining a buffer after an outage, which is legitimate and must keep working.
Attributes
- Companion
- object
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
- No credential, or one that did not verify.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalstrait ApiErrorclass Objecttrait Matchableclass AnyShow all
- The event was fine; the broker was not. Retryable, and the only failure here that is.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalstrait ApiErrorclass Objecttrait Matchableclass AnyShow all
The response to events:validate — a custom method in the sense of AIP-136.
The response to events:validate — a custom method in the sense of AIP-136.
Attributes
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
The composition root: every dependency is constructed here, once, and passed down.
The composition root: every dependency is constructed here, once, and passed down.
Explicit and not a framework. wolfram has no Guice module and no global registry because it has exactly one object graph and that graph is eleven lines long; a container would hide the two things that actually matter about it — the construction order (telemetry before anything that meters, the publisher before the service that uses it) and the reverse order in which it must be torn down.
Shutdown order is the interesting part. Stop the HTTP server first, so no new request can be accepted; then close the publisher, which drains records that clients have already been told were accepted; only then close telemetry, so the spans and metrics describing that drain are still exportable while it happens; Vert.x last, because everything above runs on its threads. Doing telemetry first is the common mistake and it makes the shutdown path the one part of the system with no observability.
Attributes
- Companion
- object
- Supertypes
-
trait StrictLoggingtrait AutoCloseableclass Objecttrait Matchableclass Any
Attributes
- Companion
- class
- Supertypes
-
class Objecttrait Matchableclass Any
- Self type
-
WolframApp.type
wolfram's whole configuration, read once at boot by the composition root.
wolfram's whole configuration, read once at boot by the composition root.
One aggregate rather than three independent lookups so that a typo in any namespace fails the process at start rather than the first time that particular subsystem is exercised — a service that boots and then 503s on its first request is much harder to diagnose than one that refuses to boot.
Attributes
- Companion
- object
- Supertypes
-
trait Serializabletrait Producttrait Equalsclass Objecttrait Matchableclass AnyShow all
Attributes
- Companion
- class
- Supertypes
-
trait Producttrait Mirrorclass Objecttrait Matchableclass Any
- Self type
-
WolframConfig.type