Runbook: Attestor - Rekor Transparency Log Unreachable
Sprint: SPRINT_20260117_029_DOCS_runbook_coverage Task: RUN-005 - Attestor Runbooks Reconciled against
src/Attestor,src/Doctor, andsrc/Clion 2026-05-31 (options, metrics, Doctor check IDs, WebService scopes/endpoints, and theattestor transparencyCLI verbs verified against source).
Queue currency note (2026-07-17). Current publication persists locally first and records optional external-sync attempts in durable mirror receipts.
RekorRetryWorkerandattestor.rekor_submission_queueare legacy drain compatibility only; no current submission producer enqueues them. Diagnose a new external outage through transparency target status/receipts and the sync runner. Use queue-depth/worker steps later in this runbook only for rows left by an older release.
Read this first — what “Rekor” means in Stella Ops. By default the Attestor’s primary transparency anchor is a Stella-owned local transparency log, not the public Sigstore instance. In
AttestorOptions(src/Attestor/.../Core/Options/AttestorOptions.cs) the default isRekor.Primary.Url = "stellaops://attestor/local-transparency"(LocalTransparencyOptions.DefaultLogUrl). This is deliberate for the offline/air-gap-first posture: a sealed site is fully functional with no external Rekor reachable. Public Sigstore Rekor (https://rekor.sigstore.dev) is only contacted when an operator explicitly pointsRekor.Primary.Url(or a mirror) at it, or when the Doctorcheck.attestation.rekor.connectivityprobe runs with its own fallback endpoint. Treat “Rekor unreachable” as “the configured external transparency endpoint is unreachable” — local anchoring keeps working regardless.
Metadata
| Field | Value |
|---|---|
| Component | Attestor |
| Severity | High (degraded to Medium when local transparency is the primary anchor) |
| On-call scope | Platform team |
| Last updated | 2026-05-31 |
| Doctor check | check.attestation.rekor.connectivity |
| Related Doctor checks | check.attestation.rekor.verification.job, check.attestation.transparency.consistency, check.attestation.clock.skew |
| Health check | rekor-verification (ASP.NET IHealthCheck, see RekorVerificationHealthCheck) |
Symptoms
- [ ] Attestation transparency logging to an external Rekor mirror failing
- [ ] Doctor
check.attestation.rekor.connectivityreportsFail(“Rekor returned …”, “Rekor connection timed out”, or “Cannot reach Rekor: …”) - [ ]
rekor-verificationhealth check reportingDegraded/Unhealthy - [ ] Rekor retry queue depth (
attestor.rekor_queue_depth) climbing - [ ] Dead-letter counter (
attestor.rekor_dead_letter_total) incrementing - [ ] Verification failing when
attestor:verification:requireTransparencyInclusion=trueand no inclusion proof is available
NOTE: There is no alert named
AttestorRekorUnavailableshipped in the repository. Wire your own Prometheus alert on the metrics above (attestor.rekor_queue_depth,attestor.rekor_dead_letter_total,attestor.rekor_submission_status_total) — see Prevention.
Impact
| Impact Type | Description |
|---|---|
| User-facing | Attestations not anchored to the external transparency log; local transparency receipts remain authoritative |
| Data integrity | Attestations are still signed and locally anchored; only external/public transparency is reduced |
| SLA / compliance | If a compliance regime requires external transparency anchoring, that requirement is degraded until the endpoint recovers |
| Verification | If requireTransparencyInclusion is enabled, verification of new entries can fail until inclusion proofs land |
Diagnosis
Quick checks
Run the Doctor connectivity check:
stella doctor --check check.attestation.rekor.connectivityThe check probes
{rekorUrl}/api/v1/log, reports latency and tree size on success, and returnsSkipin sealed/air-gap mode. The endpoint it probes is resolved from theAttestor:Rekor:Urlconfig key, falling back toTransparency:Rekor:Url, thenhttps://rekor.sigstore.dev.Key-name gotcha (verified in source): the Doctor probe key
Attestor:Rekor:Urlis not the same key the Attestor service binds for its submission anchor, which isAttestor:Rekor:Primary:Url(AttestorOptions.RekorOptions.Primary.Url). The service does not read a flatAttestor:Rekor:Url. Consequence: if you only setAttestor:Rekor:Primary:Url, the connectivity/clock-skew/consistency checks still fall through tohttps://rekor.sigstore.devand may reportFaileven though the service is healthily anchoring elsewhere (e.g. the local log). PointAttestor:Rekor:Url(orTransparency:Rekor:Url) at the same endpoint you want the Doctor checks to probe. (RekorConnectivityCheck,RekorClockSkewCheck, andTransparencyLogConsistencyCheckall read the flat key.)Check the periodic verification job health:
stella doctor --check check.attestation.rekor.verification.jobInspect the effective Attestor configuration (read-only):
stella config show attestor # 'config' is inspect-only: list / show <path>Confirm what
attestor.rekor.primary.urlandattestor.rekor.mirror.*are actually set to. Ifprimary.urlis still the defaultstellaops://attestor/local-transparency, external Rekor is not in the submission path and this incident is most likely about the verification probe or an explicitly configured mirror.
Deep diagnosis
Determine the configured primary/mirror anchor. In service config (
appsettings.jsonroot keyattestor, or env vars):attestor:rekor:primary:urlattestor:rekor:mirror:enabled/attestor:rekor:mirror:urlattestor:rekor:circuitBreaker:*(enabled by default; opens afterfailureThreshold=5failures withinfailureWindowSeconds=60)
Manually probe the configured endpoint (replace with the actual URL):
curl -s https://rekor.example.com/api/v1/log | jq . nc -zv rekor.example.com 443Check the Attestor service logs for
RekorRetryWorkeractivity. The worker logs queue depth (pending,submitting,retrying,dead_letter) and per-item submission failures. Look for repeatedRekor queue item ... submission failedwarnings andexceeded max attempts ... moved to dead lettererrors.Inspect metrics (Meter
StellaOps.Attestor):attestor.rekor_queue_depth(observable gauge) — current pending + retryingattestor.rekor_retry_attempts_total{backend,attempt}attestor.rekor_submission_status_total{status,backend}attestor.rekor_dead_letter_total{backend}attestor.submit_total{result,backend}/attestor.submit_latency_seconds
List recent attestations and their log/mirror status via the API to see which entries lack external mirror receipts:
GET /api/v1/attestations # requires attestor.read scope GET /api/v1/rekor/entries/{uuid} # set ?refresh=true to bypass cache
Scope note: the attestor HTTP endpoints authorize on the scope claim values
attestor.read,attestor.verify,attestor.write(dot form, seeAttestorWebServiceComposition). The ASP.NET authorization policy names are the colon formsattestor:read/attestor:verify/attestor:write— do not confuse the two.
Resolution
Immediate mitigation
Rely on the durable retry queue (default behavior). A failed external submission is not lost —
RekorRetryWorkerretries from the durable queue with exponential backoff. The queue is governed byRekorQueueOptions(config rootattestor; defaults:enabled=true,maxAttempts=5,initialDelayMs=1000,maxDelayMs=60000,backoffMultiplier=2.0,pollIntervalMs=5000,deadLetterRetentionDays=30). No operator action is required for retries to occur; the worker drains the queue automatically when the endpoint recovers.Let the circuit breaker fail over (default behavior).
RekorCircuitBreakerOptionsis enabled by default withfailoverToMirrorWhenOpen=trueanduseCacheWhenOpen=true. If a mirror is configured (attestor:rekor:mirror:enabled=true), traffic fails over to it while the primary circuit is open.Relax the inclusion requirement (only if compliance allows). To stop verification from failing on entries that have no inclusion proof yet, set
attestor:verification:requireTransparencyInclusion=false(and/orattestor:verification:requireCheckpoint=false) and restart the service. Warning: this reduces transparency guarantees; revert once the log recovers. (These are the real knobs inAttestorOptions.VerificationOptions; there is norekor.requiredflag.)Configure or enable a mirror if you have a second Rekor-compatible endpoint, by setting
attestor:rekor:mirror:enabled=trueandattestor:rekor:mirror:url=...and restarting the service.
The legacy commands
stella attest config set/get,stella attest reload,stella attest rekor status|ping|pending-entries|process-queue|test-submit, and the flagsrekor.queue_on_failure/rekor.required/rekor.url/rekor.proxydo not exist. Configuration changes are applied through service config (appsettings/env) plus a restart; retries are handled by the background worker, not a manualprocess-queuecommand.
Root cause fix
If this is a public/external Rekor outage:
- Confirm the outage independently (e.g. Sigstore status, your mirror’s status page). The platform is offline-first — a public Rekor outage does not block signing or local anchoring.
- Do nothing destructive. Queued submissions retry automatically; once the endpoint is healthy the worker drains the queue. Watch
attestor.rekor_queue_depthtrend back to zero.
If this is a network/firewall issue:
- Verify outbound HTTPS to the configured endpoint:
curl -v https://<configured-rekor-host>/api/v1/log --max-time 30 nc -zv <configured-rekor-host> 443 - Add the configured Rekor endpoint(s) to your egress allowlist. If you also use Sigstore keyless signing, Fulcio (
fulcio.sigstore.dev:443) is needed for certificate issuance in addition to the Rekor host. - There is no built-in HTTP-proxy config key for Rekor; configure egress at the host/network level (standard
HTTPS_PROXY/NO_PROXYenv handling applies to the .NET HTTP client).
If this is a TLS/certificate issue:
- Inspect the handshake:
openssl s_client -connect <configured-rekor-host>:443 -brief - Update the host CA trust store through your OS/base-image mechanism. (There is no
stella crypto ca updatecommand —stella cryptocovers sign/verify/profile operations, not CA bundle management.)
If the periodic verification job is failing (root-consistency / clock skew):
- A
Rekor root consistency check failedfromrekor-verificationindicates possible log tampering or a checkpoint mismatch — escalate, do not silence. Cross-checkcheck.attestation.transparency.consistency. - A high
attestor.time_skew_detected_totalorcheck.attestation.clock.skewfailure points at host clock drift; fix NTP before re-running verification.
If this is a sealed / air-gap deployment:
- The connectivity check returns
Skipwhen air-gapped — external Rekor probes are intentionally not made. Use the offline transparency exchange flow instead of an external submission.exchangeandsyncare command groups; the runnable leaves are:
Connected-window sync is gated by# Sealed-site controlled-media exchange (create -> validate -> apply): stella attestor transparency exchange schedule create ... stella attestor transparency exchange relay-import create ... stella attestor transparency exchange import validate ... stella attestor transparency exchange import apply ... # Connected-window mirror sync (must be enabled — see below): stella attestor transparency sync run ... # Inspect targets / receipts / sync reports: stella attestor transparency targets list stella attestor transparency receipts listattestor:transparency:externalSync:enabled(and a separateworkerEnabledflag) and exposed viaPOST /api/v1/transparency/external-sync:run(requiresattestor:write; returns a disabled-state problem document if the feature flag is off).
Verification
# Re-run the connectivity check (expect Pass, or Skip when air-gapped)
stella doctor --check check.attestation.rekor.connectivity
# Re-run the verification-job check
stella doctor --check check.attestation.rekor.verification.job
# Confirm the queue is draining (depth trending to zero) via your metrics backend:
# attestor.rekor_queue_depth, attestor.rekor_dead_letter_total
# Inspect a specific entry, forcing a fresh fetch (requires attestor.read scope)
GET /api/v1/rekor/entries/{uuid}?refresh=true
Prevention
- [ ] Local-first anchoring: keep
attestor:rekor:primary:urlon the local transparency log unless an external regime genuinely requires public Rekor; this makes the service resilient to external outages by design. - [ ] Mirror + failover: configure a mirror and leave the circuit breaker (
attestor:rekor:circuitBreaker) at its default-enabled state withfailoverToMirrorWhenOpen=true. - [ ] Durable queue: leave
RekorQueueOptions.enabled=trueso transient outages are absorbed by retry rather than lost. - [ ] Monitoring: alert on
attestor.rekor_queue_depthgrowth and anyattestor.rekor_dead_letter_totalincrease; alert on therekor-verificationhealth check transitioning toDegraded/Unhealthy. - [ ] Offline drills: periodically exercise the
stella attestor transparency exchange schedule/importleaves so the sealed-site exchange path is known-good before you need it.
Related Resources
- Module dossier:
docs/modules/attestor/architecture.md - Transparency design (offline inputs, local primary, rekor-receipt schema):
docs/modules/attestor/transparency.md - Periodic Rekor verification design:
docs/modules/attestor/rekor-verification-design.md - Related runbooks:
attestor-signing-failed.md,attestor-verification-failed.md,attestor-key-expired.md,attestor-hsm-connection.md - Doctor remediation articles (referenced by the checks):
docs/doctor/articles/attestor/rekor-connectivity.md,docs/doctor/articles/attestor/transparency-consistency.md - Source of truth:
src/Attestor/StellaOps.Attestor/StellaOps.Attestor.Core/Options/AttestorOptions.cs(also holdsRekorCircuitBreakerOptions,RekorMirrorOptions,VerificationOptions,ExternalTransparencySyncOptions),.../StellaOps.Attestor.Core/Options/RekorQueueOptions.cs,.../StellaOps.Attestor.Core/Verification/RekorVerificationHealthCheck.cs,.../StellaOps.Attestor.Core/Observability/AttestorMetrics.cs(metric names),src/Attestor/StellaOps.Attestor/StellaOps.Attestor.Infrastructure/Workers/RekorRetryWorker.cs(a separate.Infrastructureproject, not under.Core),src/Attestor/StellaOps.Attestor/StellaOps.Attestor.WebService/AttestorWebServiceComposition.cs+.../AttestorWebServiceEndpoints.cs(scopes + endpoints),src/Doctor/__Plugins/StellaOps.Doctor.Plugin.Attestor/Checks/RekorConnectivityCheck.cs,src/Cli/StellaOps.Cli/Commands/AttestorTransparencyCommandGroup.cs(CLI verbs)
Doc gap (flag, not invented): the prior runbook linked
docs/modules/attestor/transparency-log.mdanddocs/operations/rekor-configuration.md; neither exists in this docs tree. They have been re-pointed todocs/modules/attestor/transparency.mdand the module dossier above. If a dedicated Rekor-configuration page is desired, create it underdocs/modules/attestor/operations/.
