Skip to content

Request Logs and Traceability

Vulnerabilities are usually disclosed after the package has already been installed, sometimes months after. The question at that moment is not whether the package is bad. It is where did it go — which pipelines pulled it, into which environments, on whose behalf, and does any of that still exist.

The Logs page answers that. Every request the proxy handles is recorded, allowed and blocked alike, with the decision and the reason for it.

The Logs page filtered to blocked events over a date range, showing decision reasons and client context previews.
Request logs filtered to blocked events over a fixed window.
Field What it tells you
Timestamp When the request was handled
Event type Whether the package was downloaded or blocked
Ecosystem, package, version Exactly what was requested
Project The project the API key is bound to
Decision reason Why the request was allowed or blocked, in the policy’s own words
Correlation ID An identifier that ties together every request from one build or job
Client context Trusted metadata about where the request came from

Selecting any row opens the full record, including the complete client context.

The Log Details panel for a blocked event with Source, Environment, User, and X-Forwarded-For client context.
A single request, with the context that identifies where it came from.

Filters are built for narrowing an investigation rather than browsing:

  • Search across package, correlation ID, project, and metadata.
  • From and To for the window you care about.
  • Ecosystem, Event type, Package, Version, and Project.
  • Correlation ID to pull up every request from one build.
  • Context key and Context value to select by where a request came from, such as every request from one CI runner or one environment.

Set an auto-refresh interval when you are watching traffic live, for example while rolling out a policy change and confirming that nothing legitimate is being blocked.

The proxy sees the request, but it does not inherently know that the request came from the nightly build of a particular service on a particular runner. Client context is how that information gets attached.

Anything in the request path can add it: a CI job, an ingress controller, a service mesh, an internal gateway, or a package manager that supports custom headers. ShieldedStack records these headers by default:

Header Use it for
ShieldedStack-Source What issued the request, such as a runner pool, agent, or gateway name
ShieldedStack-Environment Which environment it belongs to, such as staging or production
ShieldedStack-User The service account or user the job runs as
X-Forwarded-For The client address as seen by your ingress

Two things are worth understanding about this list.

First, it is a deployment setting, not a per-user one. Which headers are trusted and recorded is configured when ShieldedStack is deployed, and the list can be extended with headers already in use. Headers outside that list are ignored rather than recorded, so an arbitrary client cannot stuff the audit trail with fields of its own invention.

Second, the value is only as trustworthy as the hop that set it. Context added by your ingress or your CI platform is evidence. Context added by whatever was running on the developer laptop is a self-declaration. Where this distinction matters, have the trusted hop set the header and strip any inbound copy.

If a request carries an X-Request-Id header, ShieldedStack records it as the correlation ID. If it does not, one is generated for the request.

Setting it per build is a small change with a large payoff: every package that one pipeline run pulled shares an identifier, so you can reconstruct the full dependency set of a single build from the log rather than inferring it from timestamps. Most CI systems already expose a build or run identifier that can be passed straight through.

An advisory lands for a version you allowed three months ago. A workable sequence:

  1. Establish exposure. Filter by package and version with no date bound. If there are no rows, the version never came through this workspace.
  2. Bound the window. The earliest and latest timestamps tell you how long the exposure ran, which usually decides whether this is a rebuild or an investigation.
  3. Identify the projects. The project column names the API keys, and therefore the repositories and pipelines, that pulled it.
  4. Narrow to environments. Filter by context key and value to separate a version that only ever reached a development sandbox from one that reached a production release pipeline.
  5. Reconstruct individual builds. Take a correlation ID from any row and filter on it to see everything that build pulled alongside the affected package.
  6. Stop the bleeding. Add a deny filter for the affected version, or lower the block level, then watch the log with auto-refresh on to confirm new attempts are being blocked. See Policy and Enforcement.

Steps 1 through 4 are also the fastest honest answer to “were we affected”, which is a question that tends to arrive from outside engineering and with a deadline attached.

Blocked events are worth reviewing on their own, not only during incidents. Each carries the reason the policy gave, which separates the three cases that look identical from a failing build:

  • a package that is genuinely denied,
  • a version that is merely too new and will pass once the grace period elapses, and
  • a request that could not be verified in Verify then Trust mode.

Filtering to blocked events after a policy change is the quickest way to see whether a new rule is doing what you intended or quietly breaking a team’s build. A developer reporting a failed install can usually be answered in one lookup: filter by their project and package, and read the reason.