ecluse
Safe HaskellNone
LanguageGHC2021

Ecluse.Server.Context

Description

The per-request context the serve path reads through, and the handler monad over it.

Mount dispatch (Ecluse.Server) 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 composition-root Env. A handler reads its per-mount dependencies (the classifier, the packument-serve dependencies, the error renderer, the path prefix) and the shared composition root from that one context, rather than taking them as explicit arguments threaded down the pipeline.

RequestCtx is a concrete record with plain accessors (ctxEnv, ctxMount), matching the concrete App newtype: there is no Has-class indirection. The handler monad layers over the same katip base as App, so a structured log call composes uniformly across the service and request layers.

This module sits below Ecluse.Server (dispatch) and Ecluse.Server.Pipeline (the packument handler) so both can share these types without an import cycle; the binding is constructed at the composition root (see Ecluse).

Synopsis

Packument-serve dependencies

data PackumentDeps Source #

The per-mount inputs the serve handlers need beyond the composition-root Env: 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

  • pdPrivateBaseUrl :: Text

    The private upstream base URL; under passthrough, reads forward the client's credential.

  • pdPublicBaseUrl :: Text

    The public upstream base URL; reads are anonymous (no client credential).

  • pdMountBaseUrl :: Text

    The mount's externally-visible base URL, under which served dist.tarball URLs are rewritten so artifacts are fetched back through the gate.

  • pdMirrorTarget :: Text

    The mount's mirror-target endpoint — where the demand-driven mirror worker publishes an approved artifact. Carried on the enqueued MirrorJob as its publish destination; the serve path never reads or writes it itself.

  • pdRules :: [PrecededRule]

    The mount's resolved pure rule policy, evaluated against every public version.

  • pdEffectfulRules :: [PrecededEffectfulRule]

    The mount's effectful rule policy (advisory lookups, per-version fetches), layered on the pure tier per Ecluse.Rules.Effectful. Empty when no effectful rule is configured, in which case the effectful tier is skipped and gating reduces exactly to the pure tier.

  • pdTarballHostPolicy :: TarballHostPolicy

    Whether a tarball may be fetched from a dist.tarball host that differs from the upstream that served the packument (SameHostAsPackument by default, the secure reading of the host allowlist; relaxed to AnyAllowlistedHost by PROXY_RESPECT_UPSTREAM_TARBALL_HOST).

  • pdAllowedInternalHosts :: LoweredHostSet

    The hosts deliberately opted in to the internal-range block when gating an honoured artifact location (tarballHostAllowed). This is the same per-host opt-in the guarded manager's resolved-IP recheck honours (see Ecluse.Security.Egress), carried here so the pure tarball-host gate and the connection-time recheck agree on which internal hosts are deliberate. Empty by default — the secure reading, matching the composition root's guarded manager.

  • pdLimits :: Limits

    The response-bound budget enforced on every upstream metadata fetch and decode (PROXY_MAX_RESPONSE_BYTES/PROXY_MAX_VERSION_COUNT/PROXY_MAX_NESTING_DEPTH): the body-size, version-count, and JSON-nesting ceilings of Limits. The data plane reads the metadata body through boundedRead against maxBodyBytes, checks checkNestingDepth at the JSON-decode boundary, and checkVersionCount after projection; a breach degrades the contribution to nothing (the fail-closed parse-failure path), so a pathological upstream document is refused, never partially served (security.md invariant 4).

  • pdInboundToken :: Maybe Secret

    The optional inbound token a client must present (PROXY_AUTH_TOKEN); Nothing leaves the edge open (the network layer guards it).

  • pdNow :: IO UTCTime

    The wall-clock "now" for the rules' EvalContext. Injected so the time-sensitive age gate is deterministic under test.

  • pdHelp :: Maybe HelpMessage

    The operator help message appended to every denial body, if configured.

  • pdMinIntegrity :: MinIntegrity

    The minimum integrity algorithm a public version's digest must meet to be admitted (the global PROXY_MIN_PUBLIC_INTEGRITY floor, default SHA-256). The public gate refuses a version whose strongest digest is below this; the trusted private path never consults it (see Ecluse.Package.Integrity).

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 (see Ecluse.Server).

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 composition-root Env paired with the MountBinding the request matched. A concrete record with plain accessors — ctxEnv and ctxMount — so a handler reads the shared root and its per-mount wiring from one place rather than as explicit arguments.

Dispatch builds it once per request (see Ecluse.Server); the handler reads it through the Handler reader.

Constructors

RequestCtx 

Fields

  • ctxEnv :: Env

    The composition root — the handles, the shared HTTP manager, the caches, the logger.

  • ctxMount :: MountBinding

    The mount the request matched, carrying its complete ecosystem wiring.

Instances

Instances details
MonadReader RequestCtx Handler Source # 
Instance details

Defined in Ecluse.Server.Context

The handler monad

newtype Handler a Source #

The request hot path's monad: a reader over the per-request RequestCtx layered on katip's logging context.

A newtype over ReaderT RequestCtx (KatipContextT IO) 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 (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 — over the same base as App, so the service and request layers log uniformly.

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").

Constructors

Handler 

Instances

Instances details
MonadIO Handler Source # 
Instance details

Defined in Ecluse.Server.Context

Methods

liftIO :: IO a -> Handler a #

Applicative Handler Source # 
Instance details

Defined in Ecluse.Server.Context

Methods

pure :: a -> Handler a #

(<*>) :: Handler (a -> b) -> Handler a -> Handler b #

liftA2 :: (a -> b -> c) -> Handler a -> Handler b -> Handler c #

(*>) :: Handler a -> Handler b -> Handler b #

(<*) :: Handler a -> Handler b -> Handler a #

Functor Handler Source # 
Instance details

Defined in Ecluse.Server.Context

Methods

fmap :: (a -> b) -> Handler a -> Handler b #

(<$) :: a -> Handler b -> Handler a #

Monad Handler Source # 
Instance details

Defined in Ecluse.Server.Context

Methods

(>>=) :: Handler a -> (a -> Handler b) -> Handler b #

(>>) :: Handler a -> Handler b -> Handler b #

return :: a -> Handler a #

Katip Handler Source # 
Instance details

Defined in Ecluse.Server.Context

KatipContext Handler Source # 
Instance details

Defined in Ecluse.Server.Context

MonadUnliftIO Handler Source # 
Instance details

Defined in Ecluse.Server.Context

Methods

withRunInIO :: ((forall a. Handler a -> IO a) -> IO b) -> Handler b Source #

MonadReader RequestCtx Handler Source # 
Instance details

Defined in Ecluse.Server.Context

runHandler :: RequestCtx -> Handler a -> IO a Source #

Run a Handler against the RequestCtx dispatch built for the request, yielding the underlying IO action Warp's continuation runs in. This is the boundary where the serve path's Handler code is discharged to IO.

The katip context is initialised with the request's dd object (the resolved service identity and, since the WAI server span is active by now, its trace/span ids — see Ecluse.Telemetry.Correlation), 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.