| Safe Haskell | None |
|---|---|
| Language | GHC2021 |
Ecluse.Core.Server.Context
Description
The per-request context the serve path reads through, and the handler monad over it.
Mount dispatch matches a request to one MountBinding -- a mount's complete
ecosystem wiring -- then runs the route's handler in Handler, a reader over a
RequestCtx pairing that binding with the request runtime ServeRuntime. A handler
reads its per-mount dependencies (the classifier, the packument-serve dependencies,
the error renderer, the path prefix) and the shared runtime from that one context,
rather than taking them as explicit arguments threaded down the pipeline.
ServeRuntime is the runtime interface the serve path is closed over: the two
data-plane HTTP managers, the metadata cache, the mirror queue, and the abstract
metric- and tracing-recording ports. It holds precisely what the pipeline needs to
serve a request and nothing more; the application's composition root constructs it
(wiring the concrete OpenTelemetry-backed ports), and a test constructs it over
doubles. Logging is not a field: a handler logs through the ambient katip
context, which the dispatch boundary establishes (with the structured-log scribes and
the trace-correlation dd object) when it runs the handler.
RequestCtx is a concrete record with plain accessors (ctxRuntime, ctxMount). The
handler monad layers over katip's logging context, so a structured log call composes
uniformly across the serve path.
Synopsis
- data ServeRuntime = ServeRuntime {}
- data PackumentDeps = PackumentDeps {
- pdPrivateBaseUrl :: Text
- pdPublicBaseUrl :: Text
- pdMountBaseUrl :: Text
- pdMirrorTarget :: Text
- pdRules :: [PreparedRule]
- pdTarballHostPolicy :: TarballHostPolicy
- pdAdditionalBlockedRanges :: [IPRange]
- pdTarballHostGate :: TarballHostGate
- pdLimits :: Limits
- pdInboundToken :: Maybe Secret
- pdNow :: IO UTCTime
- pdHelp :: Maybe HelpMessage
- pdMinIntegrity :: MinIntegrity
- pdMinTrustedIntegrity :: MinTrustedIntegrity
- pdNewMetadataClient :: TracingPort -> MetricsPort -> Upstream -> ManifestCaching -> (PackageName -> MetadataError -> IO ()) -> (PackageName -> [InvalidEntry] -> IO ()) -> (PackageName -> IO ()) -> Limits -> Manager -> Text -> Maybe Secret -> MetadataClient
- pdBuildArtifactRequestByFile :: Limits -> Manager -> Text -> Maybe Secret -> PackageName -> Text -> Either UrlFormationError Request
- pdBuildArtifactRequestByUrl :: Limits -> Manager -> Text -> Maybe Secret -> Text -> Either UrlFormationError Request
- pdAssemble :: Text -> Map SourceId Value -> MergePlan -> Value -> Value
- data PublishDeps = PublishDeps {
- pubTargetUrl :: Text
- pubScopes :: [Scope]
- pubStaticToken :: Maybe Secret
- pubInboundToken :: Maybe Secret
- pubLimits :: Limits
- pubHelp :: Maybe HelpMessage
- pubRelayPublish :: Limits -> Manager -> Text -> Maybe Secret -> PackageName -> ByteString -> IO (Either UrlFormationError PublishRelayResponse)
- pubCanonicaliseName :: Text -> Maybe PackageName
- data MountBinding = MountBinding {}
- data RequestCtx = RequestCtx {}
- data Handler a
- runHandler :: LogEnv -> SimpleLogPayload -> RequestCtx -> Handler a -> IO a
Request runtime
data ServeRuntime Source #
The runtime backends the serve path is closed over: exactly the effectful
capabilities a request needs to fetch, gate, serve, and record. A record of concrete
handles and abstract ports (the Handle pattern), assembled by the composition root and
read by every handler through the RequestCtx.
The two HTTP managers carry the per-origin split: the public manager serves the
untrusted public-upstream and artifact egress, the private manager the trusted
private-upstream path. Both are the validating TLS manager (registry egress is
https-only by construction; certificate validation authenticates the host), so the
split is in credential handling and the dist.tarball host gate's trust, not the
manager. The metadata cache and mirror queue are the shared data-plane handles. The
metric and tracing ports are the abstract recording interfaces
(Ecluse.Core.Telemetry.Record, Ecluse.Core.Telemetry.Span); the application supplies
their OpenTelemetry-backed implementations, so the serve path records without naming a
telemetry backend. There is no log field: handlers log through the ambient katip
context.
Constructors
| ServeRuntime | |
Fields
| |
Packument-serve dependencies
data PackumentDeps Source #
The per-mount inputs the serve handlers need beyond the request runtime
ServeRuntime: the two upstream endpoints, the mount's externally-visible base URL,
the mirror-target endpoint, its resolved rule policy, the edge auth token, the
wall-clock source, and the operator help message.
These are a mount-level concern, resolved at the composition root (a separate
concern) and carried on the mount's MountBinding; a handler reads exactly what it
needs to decide and serve from the RequestCtx it runs in. Both the packument and
the tarball paths share these deps -- the tarball path additionally gates one
version and enqueues a mirror job to pdMirrorTarget -- so the name is retained for
continuity rather than narrowed to one route.
Constructors
| PackumentDeps | |
Fields
| |
Publish-serve dependencies
data PublishDeps Source #
The per-mount inputs the first-party publish handler needs: the publication target endpoint, the publish-scope allow-list (the anti-shadowing guard), the optional static fallback credential, the edge token, the response-bound budget, and the operator help message.
The mere presence of these deps is the publish path's opt-in: a mount carries a
PublishDeps only when a publication target is configured, so the binding's
bindingPublishDeps being Nothing is exactly the "no publication target ⇒ a
PUT /{pkg} is 405 Method Not Allowed" rule, modelled in the type rather than
re-derived at the handler (see
docs/architecture/registry-model.md → "Publishing first-party packages").
The credential posture is passthrough, symmetric with the private-upstream read
under passthrough: the publisher's own forwarded token is what reaches the
publication target, the static pubStaticToken only a fallback for a client that
sends none. Écluse mints no token of its own here -- unlike the mirror target -- so this
record carries no CredentialProvider (see
docs/architecture/access-model.md → "Publishing: the publication target").
Constructors
| PublishDeps | |
Fields
| |
Mount binding
data MountBinding Source #
A mount: a path prefix bound to a registry, carrying that registry's
complete ecosystem wiring. Dispatch matches a request's leading path segments
to bindingPrefix, strips them, and routes the remainder through the rest of the
binding.
The prefix is a NonEmpty list of segments ("npm" :| [] for a /npm mount):
every registry is path-mounted, so a root mount -- which would force a URL change
on every consumer the day a second ecosystem is added -- is unrepresentable
rather than merely discouraged. Bundling the classifier, serve dependencies, and
renderer into one record means a mount cannot be half-wired: there is no default
to fall back to.
Constructors
| MountBinding | |
Fields
| |
Per-request context
data RequestCtx Source #
The context one request is served through: the request runtime ServeRuntime
paired with the MountBinding the request matched. A concrete record with plain
accessors -- ctxRuntime and ctxMount -- so a handler reads the shared runtime and its
per-mount wiring from one place rather than as explicit arguments.
Dispatch builds it once per request; the handler reads it through the Handler reader.
Constructors
| RequestCtx | |
Fields
| |
Instances
| MonadReader RequestCtx Handler Source # | |
Defined in Ecluse.Core.Server.Context Methods ask :: Handler RequestCtx # local :: (RequestCtx -> RequestCtx) -> Handler a -> Handler a # reader :: (RequestCtx -> a) -> Handler a # | |
The handler monad
The request hot path's monad: a reader over the per-request RequestCtx
layered on katip's logging context.
A newtype over so its instances
are this module's to control and call sites name one concrete monad. The derived
instances give reader access to the context (ReaderT RequestCtx (KatipContextT IO)MonadReader RequestCtx), arbitrary
effects (MonadIO), the unlift capability (MonadUnliftIO) the serve path's
concurrently/bracket need, and the katip classes (Katip, KatipContext)
so a structured log call composes through the ambient context the dispatch boundary
establishes.
The katip base is a reader, never a StateT, so logging context behaves
correctly across the serve path's concurrent fetches (see
docs/architecture/technology-stack.md → "Key Decisions").
Instances
| MonadIO Handler Source # | |
Defined in Ecluse.Core.Server.Context | |
| Applicative Handler Source # | |
| Functor Handler Source # | |
| Monad Handler Source # | |
| Katip Handler Source # | |
| KatipContext Handler Source # | |
Defined in Ecluse.Core.Server.Context Methods getKatipContext :: Handler LogContexts Source # localKatipContext :: (LogContexts -> LogContexts) -> Handler a -> Handler a Source # getKatipNamespace :: Handler Namespace Source # localKatipNamespace :: (Namespace -> Namespace) -> Handler a -> Handler a Source # | |
| MonadUnliftIO Handler Source # | |
Defined in Ecluse.Core.Server.Context | |
| MonadReader RequestCtx Handler Source # | |
Defined in Ecluse.Core.Server.Context Methods ask :: Handler RequestCtx # local :: (RequestCtx -> RequestCtx) -> Handler a -> Handler a # reader :: (RequestCtx -> a) -> Handler a # | |
runHandler :: LogEnv -> SimpleLogPayload -> RequestCtx -> Handler a -> IO a Source #
Run a Handler against the RequestCtx dispatch built for the request and the
katip logging environment and initial context the dispatch boundary supplies,
yielding the underlying IO action the server's continuation runs in. This is the
boundary where the serve path's Handler code is discharged to IO.
The LogEnv (the structured-log scribes) and the initial context payload are passed
in rather than read from the runtime, so the application owns the log stream and the
trace-correlation dd enrichment: it resolves the dd object for the request and
hands it here as the initial context, so every line a handler emits carries dd for
trace-to-log correlation. A handler narrows the namespace or adds package/version/rule
context with katip's combinators on top as it logs.