Add channel test providers for Email, Slack, Teams, and Webhook
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- Implemented EmailChannelTestProvider to generate email preview payloads. - Implemented SlackChannelTestProvider to create Slack message previews. - Implemented TeamsChannelTestProvider for generating Teams Adaptive Card previews. - Implemented WebhookChannelTestProvider to create webhook payloads. - Added INotifyChannelTestProvider interface for channel-specific preview generation. - Created ChannelTestPreviewContracts for request and response models. - Developed NotifyChannelTestService to handle test send requests and generate previews. - Added rate limit policies for test sends and delivery history. - Implemented unit tests for service registration and binding. - Updated project files to include necessary dependencies and configurations.
This commit is contained in:
@@ -522,10 +522,12 @@ See `docs/dev/32_AUTH_CLIENT_GUIDE.md` for recommended profiles (online vs. air-
|
||||
| `stellaops-cli auth revoke export` | Export the Authority revocation bundle | `--output <directory>` (defaults to CWD) | Writes `revocation-bundle.json`, `.json.jws`, and `.json.sha256`; verifies the digest locally and includes key metadata in the log summary. |
|
||||
| `stellaops-cli auth revoke verify` | Validate a revocation bundle offline | `--bundle <path>` `--signature <path>` `--key <path>`<br>`--verbose` | Verifies detached JWS signatures, reports the computed SHA-256, and can fall back to cached JWKS when `--key` is omitted. |
|
||||
| `stellaops-cli config show` | Display resolved configuration | — | Masks secret values; helpful for air‑gapped installs |
|
||||
| `stellaops-cli runtime policy test` | Ask Scanner.WebService for runtime verdicts (Webhook parity) | `--image/-i <digest>` (repeatable, comma/space lists supported)<br>`--file/-f <path>`<br>`--namespace/--ns <name>`<br>`--label/-l key=value` (repeatable)<br>`--json` | Posts to `POST /api/v1/scanner/policy/runtime`, deduplicates image digests, and prints TTL + per-image verdict/signed/SBOM status. Accepts newline/whitespace-delimited stdin when piped; `--json` emits the raw response without additional logging. |
|
||||
| `stellaops-cli runtime policy test` | Ask Scanner.WebService for runtime verdicts (Webhook parity) | `--image/-i <digest>` (repeatable, comma/space lists supported)<br>`--file/-f <path>`<br>`--namespace/--ns <name>`<br>`--label/-l key=value` (repeatable)<br>`--json` | Posts to `POST /api/v1/scanner/policy/runtime`, deduplicates image digests, and prints TTL/policy revision plus per-image columns for signed state, SBOM referrers, quieted-by metadata, confidence, and Rekor attestation (uuid + verified flag). Accepts newline/whitespace-delimited stdin when piped; `--json` emits the raw response without additional logging. |
|
||||
|
||||
When running on an interactive terminal without explicit override flags, the CLI uses Spectre.Console prompts to let you choose per-run ORAS/offline bundle behaviour.
|
||||
|
||||
Runtime verdict output reflects the SCANNER-RUNTIME-12-302 contract sign-off (quieted provenance, confidence band, attestation verification). CLI-RUNTIME-13-008 now mirrors those fields in both table and `--json` formats.
|
||||
|
||||
**Startup diagnostics**
|
||||
|
||||
- `stellaops-cli` now loads Authority plug-in manifests during startup (respecting `Authority:Plugins:*`) and surfaces analyzer warnings when a plug-in weakens the baseline password policy (minimum length **12** and all character classes required).
|
||||
|
||||
@@ -12,7 +12,7 @@ runtime wiring, CLI usage) and leaves connector/internal customization for later
|
||||
- .NET SDK **10.0.100-preview** (matches `global.json`)
|
||||
- MongoDB instance reachable from the host (local Docker or managed)
|
||||
- `trivy-db` binary on `PATH` for Trivy exports (and `oras` if publishing to OCI)
|
||||
- Plugin assemblies present in `PluginBinaries/` (already included in the repo)
|
||||
- Plugin assemblies present in `StellaOps.Concelier.PluginBinaries/` (already included in the repo)
|
||||
- Optional: Docker/Podman runtime if you plan to run scanners locally
|
||||
|
||||
> **Tip** – air-gapped installs should preload `trivy-db` and `oras` binaries into the
|
||||
@@ -31,7 +31,7 @@ runtime wiring, CLI usage) and leaves connector/internal customization for later
|
||||
```
|
||||
|
||||
2. Edit `etc/concelier.yaml` and update the MongoDB DSN (and optional database name).
|
||||
The default template configures plug-in discovery to look in `PluginBinaries/`
|
||||
The default template configures plug-in discovery to look in `StellaOps.Concelier.PluginBinaries/`
|
||||
and disables remote telemetry exporters by default.
|
||||
|
||||
3. (Optional) Override settings via environment variables. All keys are prefixed with
|
||||
@@ -71,7 +71,7 @@ runtime wiring, CLI usage) and leaves connector/internal customization for later
|
||||
environment. Authority expects per-plugin manifests in `etc/authority.plugins/`;
|
||||
sample `standard.yaml` and `ldap.yaml` files are provided as starting points.
|
||||
For air-gapped installs keep the default plug-in binary directory
|
||||
(`../PluginBinaries/Authority`) so packaged plug-ins load without outbound access.
|
||||
(`../StellaOps.Authority.PluginBinaries`) so packaged plug-ins load without outbound access.
|
||||
|
||||
3. Environment variables prefixed with `STELLAOPS_AUTHORITY_` override individual
|
||||
fields. Example:
|
||||
|
||||
@@ -82,28 +82,53 @@ Add this to **`MyPlugin.Schedule.csproj`** so the signed DLL + `.sig` land in th
|
||||
|
||||
---
|
||||
|
||||
## 5 Dependency‑Injection Entry‑point
|
||||
|
||||
Back‑end auto‑discovers the static method below:
|
||||
|
||||
~~~csharp
|
||||
namespace StellaOps.DependencyInjection;
|
||||
|
||||
public static class IoCConfigurator
|
||||
{
|
||||
public static IServiceCollection Configure(this IServiceCollection services,
|
||||
IConfiguration cfg)
|
||||
{
|
||||
services.AddSingleton<IJob, MyJob>(); // schedule job
|
||||
services.Configure<MyPluginOptions>(cfg.GetSection("Plugins:MyPlugin"));
|
||||
return services;
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
---
|
||||
|
||||
## 6 Schedule Plug‑ins
|
||||
## 5 Dependency‑Injection Entry‑point
|
||||
|
||||
Back‑end auto‑discovers restart‑time bindings through two mechanisms:
|
||||
|
||||
1. **Service binding metadata** for simple contracts.
|
||||
2. **`IDependencyInjectionRoutine`** implementations when you need full control.
|
||||
|
||||
### 5.1 Service binding metadata
|
||||
|
||||
Annotate implementations with `[ServiceBinding]` to declare their lifetime and service contract.
|
||||
The loader honours scoped lifetimes and will register the service before executing any custom DI routines.
|
||||
|
||||
~~~csharp
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using StellaOps.DependencyInjection;
|
||||
|
||||
[ServiceBinding(typeof(IJob), ServiceLifetime.Scoped, RegisterAsSelf = true)]
|
||||
public sealed class MyJob : IJob
|
||||
{
|
||||
// IJob dependencies can now use scoped services (Mongo sessions, etc.)
|
||||
}
|
||||
~~~
|
||||
|
||||
Use `RegisterAsSelf = true` when you also want to resolve the concrete type.
|
||||
Set `ReplaceExisting = true` to override default descriptors if the host already provides one.
|
||||
|
||||
### 5.2 Dependency injection routines
|
||||
|
||||
For advanced scenarios continue to expose a routine:
|
||||
|
||||
~~~csharp
|
||||
namespace StellaOps.DependencyInjection;
|
||||
|
||||
public sealed class IoCConfigurator : IDependencyInjectionRoutine
|
||||
{
|
||||
public IServiceCollection Register(IServiceCollection services, IConfiguration cfg)
|
||||
{
|
||||
services.AddSingleton<IJob, MyJob>(); // schedule job
|
||||
services.Configure<MyPluginOptions>(cfg.GetSection("Plugins:MyPlugin"));
|
||||
return services;
|
||||
}
|
||||
}
|
||||
~~~
|
||||
|
||||
---
|
||||
|
||||
## 6 Schedule Plug‑ins
|
||||
|
||||
### 6.1 Minimal Job
|
||||
|
||||
@@ -191,4 +216,4 @@ On merge, the plug‑in shows up in the UI Marketplace.
|
||||
| NotDetected | .sig missing | cosign sign … |
|
||||
| VersionGateMismatch | Backend 2.1 vs plug‑in 2.0 | Re‑compile / bump attribute |
|
||||
| FileLoadException | Duplicate | StellaOps.Common Ensure PrivateAssets="all" |
|
||||
| Redis | timeouts Large writes | Batch or use Mongo |
|
||||
| Redis | timeouts Large writes | Batch or use Mongo |
|
||||
|
||||
@@ -147,6 +147,20 @@ All administrative calls emit `AuthEventRecord` entries enriched with correlatio
|
||||
| Security | `security.rateLimiting` | Fixed-window limits for `/token`, `/authorize`, `/internal/*`. | See `docs/security/rate-limits.md` for tuning. |
|
||||
| Bootstrap | `bootstrap.apiKey` | Shared secret required for `/internal/*`. | Only required when `bootstrap.enabled` is true. |
|
||||
|
||||
### 7.1 Sender-constrained clients (DPoP & mTLS)
|
||||
|
||||
Authority now understands two flavours of sender-constrained OAuth clients:
|
||||
|
||||
- **DPoP proof-of-possession** – clients sign a `DPoP` header for `/token` requests. Authority validates the JWK thumbprint, HTTP method/URI, and replay window, then stamps the resulting access token with `cnf.jkt` so downstream services can verify the same key is reused.
|
||||
- Configure under `security.senderConstraints.dpop`. `allowedAlgorithms`, `proofLifetime`, and `replayWindow` are enforced at validation time.
|
||||
- `security.senderConstraints.dpop.nonce.enabled` enables nonce challenges for high-value audiences (`requiredAudiences`, normalised to case-insensitive strings). When a nonce is required but missing or expired, `/token` replies with `WWW-Authenticate: DPoP error="use_dpop_nonce"` (and, when available, a fresh `DPoP-Nonce` header). Clients must retry with the issued nonce embedded in the proof.
|
||||
- `security.senderConstraints.dpop.nonce.store` selects `memory` (default) or `redis`. When `redis` is configured, set `security.senderConstraints.dpop.nonce.redisConnectionString` so replicas share nonce issuance and high-value clients avoid replay gaps during failover.
|
||||
- Declare client `audiences` in bootstrap manifests or plug-in provisioning metadata; Authority now defaults the token `aud` claim and `resource` indicator from this list, which is also used to trigger nonce enforcement for audiences such as `signer` and `attestor`.
|
||||
- **Mutual TLS clients** – client registrations may declare an mTLS binding (`senderConstraint: mtls`). When enabled via `security.senderConstraints.mtls`, Authority validates the presented client certificate against stored bindings (`certificateBindings[]`), optional chain verification, and timing windows. Successful requests embed `cnf.x5t#S256` into the access token so resource servers can enforce the certificate thumbprint.
|
||||
- Certificate bindings record the certificate thumbprint, optional SANs, subject/issuer metadata, and activation windows. Operators can enforce subject regexes, SAN type allow-lists (`dns`, `uri`, `ip`), trusted certificate authorities, and rotation grace via `security.senderConstraints.mtls.*`.
|
||||
|
||||
Both modes persist additional metadata in `authority_tokens`: `senderConstraint` records the enforced policy, while `senderKeyThumbprint` stores the DPoP JWK thumbprint or mTLS certificate hash captured at issuance. Downstream services can rely on these fields (and the corresponding `cnf` claim) when auditing offline copies of the token store.
|
||||
|
||||
## 8. Offline & Sovereign Operation
|
||||
- **No outbound dependencies:** Authority only contacts MongoDB and local plugins. Discovery and JWKS are cached by clients with offline tolerances (`AllowOfflineCacheFallback`, `OfflineCacheTolerance`). Operators should mirror these responses for air-gapped use.
|
||||
- **Structured logging:** Every revocation export, signing rotation, bootstrap action, and token issuance emits structured logs with `traceId`, `client_id`, `subjectId`, and `network.remoteIp` where applicable. Mirror logs to your SIEM to retain audit trails without central connectivity.
|
||||
|
||||
@@ -200,7 +200,8 @@ Indexes:
|
||||
* Predicate `predicateType` must be on allowlist (sbom/report/vex-export).
|
||||
* `subject.digest.sha256` values must be present and well‑formed (hex).
|
||||
* **No public submission** path. **Never** accept bundles from untrusted clients.
|
||||
* **Rate limits**: per mTLS thumbprint/license (from Signer‑forwarded claims) to avoid flooding the log.
|
||||
* **Client certificate allowlists**: optional `security.mtls.allowedSubjects` / `allowedThumbprints` tighten peer identity checks beyond CA pinning.
|
||||
* **Rate limits**: token-bucket per caller derived from `quotas.perCaller` (QPS/burst) returns `429` + `Retry-After` when exceeded.
|
||||
* **Redaction**: Attestor never logs secret material; DSSE payloads **should** be public by design (SBOMs/reports). If customers require redaction, enforce policy at Signer (predicate minimization) **before** Attestor.
|
||||
|
||||
---
|
||||
@@ -233,6 +234,10 @@ Indexes:
|
||||
* `attestor.dedupe_hits_total`
|
||||
* `attestor.errors_total{type}`
|
||||
|
||||
**Correlation**:
|
||||
|
||||
* HTTP callers may supply `X-Correlation-Id`; Attestor will echo the header and push `CorrelationId` into the log scope for cross-service tracing.
|
||||
|
||||
**Tracing**:
|
||||
|
||||
* Spans: `validate`, `rekor.submit`, `rekor.poll`, `persist`, `archive`, `verify`.
|
||||
|
||||
@@ -229,6 +229,8 @@ GET /admin/metrics # Prometheus exposition (token issue rates,
|
||||
GET /admin/healthz|readyz # health/readiness
|
||||
```
|
||||
|
||||
Declared client `audiences` flow through to the issued JWT `aud` claim and the token request's `resource` indicators. Authority relies on this metadata to enforce DPoP nonce challenges for `signer`, `attestor`, and other high-value services without requiring clients to repeat the audience parameter on every request.
|
||||
|
||||
---
|
||||
|
||||
## 11) Integration hard lines (what resource servers must enforce)
|
||||
@@ -286,6 +288,8 @@ authority:
|
||||
nonce:
|
||||
enable: true
|
||||
ttlSeconds: 600
|
||||
store: redis
|
||||
redisConnectionString: "redis://authority-redis:6379?ssl=false"
|
||||
mtls:
|
||||
enable: true
|
||||
caBundleFile: /etc/ssl/mtls/clients-ca.pem
|
||||
|
||||
@@ -94,6 +94,34 @@ mergedAt
|
||||
inputs[] // source doc digests that contributed
|
||||
```
|
||||
|
||||
**AdvisoryStatement (event log)**
|
||||
|
||||
```
|
||||
statementId // GUID (immutable)
|
||||
vulnerabilityKey // canonical advisory key (e.g., CVE-2025-12345)
|
||||
advisoryKey // merge snapshot advisory key (may reference variant)
|
||||
statementHash // canonical hash of advisory payload
|
||||
asOf // timestamp of snapshot (UTC)
|
||||
recordedAt // persistence timestamp (UTC)
|
||||
inputDocuments[] // document IDs contributing to the snapshot
|
||||
payload // canonical advisory document (BSON / canonical JSON)
|
||||
```
|
||||
|
||||
**AdvisoryConflict**
|
||||
|
||||
```
|
||||
conflictId // GUID
|
||||
vulnerabilityKey // canonical advisory key
|
||||
conflictHash // deterministic hash of conflict payload
|
||||
asOf // timestamp aligned with originating statement set
|
||||
recordedAt // persistence timestamp
|
||||
statementIds[] // related advisoryStatement identifiers
|
||||
details // structured conflict explanation / merge reasoning
|
||||
```
|
||||
|
||||
- `AdvisoryEventLog` (Concelier.Core) provides the public API for appending immutable statements/conflicts and querying replay history. Inputs are normalized by trimming and lower-casing `vulnerabilityKey`, serializing advisories with `CanonicalJsonSerializer`, and computing SHA-256 hashes (`statementHash`, `conflictHash`) over the canonical JSON payloads. Consumers can replay by key with an optional `asOf` filter to obtain deterministic snapshots ordered by `asOf` then `recordedAt`.
|
||||
- Concelier.WebService exposes the immutable log via `GET /concelier/advisories/{vulnerabilityKey}/replay[?asOf=UTC_ISO8601]`, returning the latest statements (with hex-encoded hashes) and any conflict explanations for downstream exporters and APIs.
|
||||
|
||||
**ExportState**
|
||||
|
||||
```
|
||||
@@ -252,6 +280,7 @@ public interface IFeedConnector {
|
||||
```
|
||||
* Optional ORAS push (OCI layout) for registries.
|
||||
* Offline kit bundles include Trivy DB + JSON tree + export manifest.
|
||||
* Mirror-ready bundles: when `concelier.trivy.mirror` defines domains, the exporter emits `mirror/index.json` plus per-domain `manifest.json`, `metadata.json`, and `db.tar.gz` files with SHA-256 digests so Concelier mirrors can expose domain-scoped download endpoints.
|
||||
|
||||
### 7.3 Hand‑off to Signer/Attestor (optional)
|
||||
|
||||
@@ -286,6 +315,10 @@ GET /jobs/{id} → job status
|
||||
POST /exports/json { full?:bool, force?:bool, attest?:bool } → { exportId, digest, rekor? }
|
||||
POST /exports/trivy { full?:bool, force?:bool, publish?:bool, attest?:bool } → { exportId, digest, rekor? }
|
||||
GET /exports/{id} → export metadata (kind, digest, createdAt, rekor?)
|
||||
GET /concelier/exports/index.json → mirror index describing available domains/bundles
|
||||
GET /concelier/exports/mirror/{domain}/manifest.json
|
||||
GET /concelier/exports/mirror/{domain}/bundle.json
|
||||
GET /concelier/exports/mirror/{domain}/bundle.json.jws
|
||||
```
|
||||
|
||||
**Search (operator debugging)**
|
||||
|
||||
@@ -86,6 +86,7 @@ At startup, services **self‑advertise** their semver & channel; the UI surface
|
||||
|
||||
* **Primary**: `registry.stella-ops.org` (OCI v2, supports Referrers API).
|
||||
* **Mirrors**: GHCR (read‑only), regional mirrors for latency.
|
||||
* Operational runbook: see `docs/ops/concelier-mirror-operations.md` for deployment profiles, CDN guidance, and sync automation.
|
||||
* **Pull by digest only** in Kubernetes/Compose manifests.
|
||||
|
||||
**Gating policy**:
|
||||
@@ -335,7 +336,8 @@ Prometheus + OTLP; Grafana dashboards ship in the charts.
|
||||
|
||||
* **Vulnerability response**:
|
||||
|
||||
* Concelier red‑flag advisories trigger accelerated **stable** patch rollout; UI/CLI “security patch available” notice.
|
||||
* Concelier red-flag advisories trigger accelerated **stable** patch rollout; UI/CLI “security patch available” notice.
|
||||
* 2025-10: Pinned `MongoDB.Driver` **3.5.0** and `SharpCompress` **0.41.0** across services (DEVOPS-SEC-10-301) to eliminate NU1902/NU1903 warnings surfaced during scanner cache/worker test runs; future dependency bumps follow the same central override pattern.
|
||||
|
||||
* **Backups/DR**:
|
||||
|
||||
|
||||
@@ -482,7 +482,13 @@ Run the ingestion endpoint once after applying migration `20251019-consensus-sig
|
||||
|
||||
---
|
||||
|
||||
## 17) Appendix — canonical JSON (stable ordering)
|
||||
## 17) Operational runbooks
|
||||
|
||||
* **Statement backfill** — see `docs/dev/EXCITITOR_STATEMENT_BACKFILL.md` for the CLI workflow, required permissions, observability guidance, and rollback steps.
|
||||
|
||||
---
|
||||
|
||||
## 18) Appendix — canonical JSON (stable ordering)
|
||||
|
||||
All exports and consensus entries are serialized via `VexCanonicalJsonSerializer`:
|
||||
|
||||
|
||||
@@ -37,6 +37,8 @@ src/
|
||||
**Dependencies**: Authority (OpToks; DPoP/mTLS), MongoDB, Redis/NATS (bus), HTTP egress to Slack/Teams/Webhooks, SMTP relay for Email.
|
||||
|
||||
> **Configuration.** Notify.WebService bootstraps from `notify.yaml` (see `etc/notify.yaml.sample`). Use `storage.driver: mongo` with a production connection string; the optional `memory` driver exists only for tests. Authority settings follow the platform defaults—when running locally without Authority, set `authority.enabled: false` and supply `developmentSigningKey` so JWTs can be validated offline.
|
||||
>
|
||||
> `api.rateLimits` exposes token-bucket controls for delivery history queries and test-send previews (`deliveryHistory`, `testSend`). Default values allow generous browsing while preventing accidental bursts; operators can relax/tighten the buckets per deployment.
|
||||
|
||||
> **Plug-ins.** All channel connectors are packaged under `<baseDirectory>/plugins/notify`. The ordered load list must start with Slack/Teams before Email/Webhook so chat-first actions are registered deterministically for Offline Kit bundles:
|
||||
>
|
||||
@@ -204,6 +206,8 @@ public interface INotifyConnector {
|
||||
|
||||
**DeliveryContext** includes **rendered content** and **raw event** for audit.
|
||||
|
||||
**Test-send previews.** Plug-ins can optionally implement `INotifyChannelTestProvider` to shape `/channels/{id}/test` responses. Providers receive a sanitised `ChannelTestPreviewContext` (channel, tenant, target, timestamp, trace) and return a `NotifyDeliveryRendered` preview + metadata. When no provider is present, the host falls back to a generic preview so the endpoint always responds.
|
||||
|
||||
**Secrets**: `ChannelConfig.secretRef` points to Authority‑managed secret handle or K8s Secret path; workers load at send-time; plug-in manifests (`notify-plugin.json`) declare capabilities and version.
|
||||
|
||||
---
|
||||
@@ -294,7 +298,7 @@ Internal tooling can hit `/internal/notify/<entity>/normalize` to upgrade legacy
|
||||
* **Channels**
|
||||
|
||||
* `POST /channels` | `GET /channels` | `GET /channels/{id}` | `PATCH /channels/{id}` | `DELETE /channels/{id}`
|
||||
* `POST /channels/{id}/test` → send sample message (no rule evaluation)
|
||||
* `POST /channels/{id}/test` → send sample message (no rule evaluation); returns `202 Accepted` with rendered preview + metadata (base keys: `channelType`, `target`, `previewProvider`, `traceId` + connector-specific entries); governed by `api.rateLimits:testSend`.
|
||||
* `GET /channels/{id}/health` → connector self‑check
|
||||
|
||||
* **Rules**
|
||||
@@ -305,7 +309,7 @@ Internal tooling can hit `/internal/notify/<entity>/normalize` to upgrade legacy
|
||||
* **Deliveries**
|
||||
|
||||
* `POST /deliveries` → ingest worker delivery state (idempotent via `deliveryId`).
|
||||
* `GET /deliveries?since=...&status=...&limit=...` → list (most recent first)
|
||||
* `GET /deliveries?since=...&status=...&limit=...` → list envelope `{ items, count, continuationToken }` (most recent first); base metadata keys match the test-send response (`channelType`, `target`, `previewProvider`, `traceId`); rate-limited via `api.rateLimits.deliveryHistory`. See `docs/notify/samples/notify-delivery-list-response.sample.json`.
|
||||
* `GET /deliveries/{id}` → detail (redacted body + metadata)
|
||||
* `POST /deliveries/{id}/retry` → force retry (admin, future sprint)
|
||||
|
||||
|
||||
@@ -166,6 +166,28 @@ export interface VexConsensus {
|
||||
}
|
||||
```
|
||||
|
||||
*Upcoming:* `NotifyApi` consumes delivery history using the new paginated envelope returned by `/api/v1/notify/deliveries`.
|
||||
|
||||
```ts
|
||||
export interface NotifyDeliveryListResponse {
|
||||
items: NotifyDelivery[];
|
||||
count: number;
|
||||
continuationToken?: string;
|
||||
}
|
||||
|
||||
export interface NotifyDelivery {
|
||||
deliveryId: string;
|
||||
ruleId: string;
|
||||
actionId: string;
|
||||
status: 'pending'|'sent'|'failed'|'throttled'|'digested'|'dropped';
|
||||
rendered: NotifyDeliveryRendered;
|
||||
metadata: Record<string, string>; // includes channelType, target, previewProvider, traceId, and provider-specific entries
|
||||
createdAt: string;
|
||||
sentAt?: string;
|
||||
completedAt?: string;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6) State, caching & real‑time
|
||||
|
||||
@@ -72,16 +72,17 @@ Everything here is open‑source and versioned — when you check out a git ta
|
||||
- **21 – [Install Guide](21_INSTALL_GUIDE.md)**
|
||||
- **22 – [CI/CD Recipes Library](ci/20_CI_RECIPES.md)**
|
||||
- **23 – [FAQ](23_FAQ_MATRIX.md)**
|
||||
- **24 – [Offline Update Kit Admin Guide](24_OFFLINE_KIT.md)**
|
||||
- **25 – [Concelier Apple Connector Operations](ops/concelier-apple-operations.md)**
|
||||
- **26 – [Authority Key Rotation Playbook](ops/authority-key-rotation.md)**
|
||||
- **27 – [Concelier CCCS Connector Operations](ops/concelier-cccs-operations.md)**
|
||||
- **28 – [Concelier CISA ICS Connector Operations](ops/concelier-icscisa-operations.md)**
|
||||
- **29 – [Concelier CERT-Bund Connector Operations](ops/concelier-certbund-operations.md)**
|
||||
- **30 – [Concelier MSRC Connector – AAD Onboarding](ops/concelier-msrc-operations.md)**
|
||||
|
||||
### Legal & licence
|
||||
- **31 – [Legal & Quota FAQ](29_LEGAL_FAQ_QUOTA.md)**
|
||||
- **24 – [Offline Update Kit Admin Guide](24_OFFLINE_KIT.md)**
|
||||
- **25 – [Mirror Operations Runbook](ops/concelier-mirror-operations.md)**
|
||||
- **26 – [Concelier Apple Connector Operations](ops/concelier-apple-operations.md)**
|
||||
- **27 – [Authority Key Rotation Playbook](ops/authority-key-rotation.md)**
|
||||
- **28 – [Concelier CCCS Connector Operations](ops/concelier-cccs-operations.md)**
|
||||
- **29 – [Concelier CISA ICS Connector Operations](ops/concelier-icscisa-operations.md)**
|
||||
- **30 – [Concelier CERT-Bund Connector Operations](ops/concelier-certbund-operations.md)**
|
||||
- **31 – [Concelier MSRC Connector – AAD Onboarding](ops/concelier-msrc-operations.md)**
|
||||
|
||||
### Legal & licence
|
||||
- **32 – [Legal & Quota FAQ](29_LEGAL_FAQ_QUOTA.md)**
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ Authority plug-ins extend the **StellaOps Authority** service with custom identi
|
||||
Authority hosts follow a deterministic plug-in lifecycle. The exported diagram (`docs/assets/authority/authority-plugin-lifecycle.svg`) mirrors the steps below; regenerate it from the Mermaid source if you update the flow.
|
||||
|
||||
1. **Configuration load** – `AuthorityPluginConfigurationLoader` resolves YAML manifests under `etc/authority.plugins/`.
|
||||
2. **Assembly discovery** – the shared `PluginHost` scans `PluginBinaries/Authority` for `StellaOps.Authority.Plugin.*.dll` assemblies.
|
||||
2. **Assembly discovery** – the shared `PluginHost` scans `StellaOps.Authority.PluginBinaries` for `StellaOps.Authority.Plugin.*.dll` assemblies.
|
||||
3. **Registrar execution** – each assembly is searched for `IAuthorityPluginRegistrar` implementations. Registrars bind options, register services, and optionally queue bootstrap tasks.
|
||||
4. **Runtime** – the host resolves `IIdentityProviderPlugin` instances, uses capability metadata to decide which OAuth grants to expose, and invokes health checks for readiness endpoints.
|
||||
|
||||
@@ -177,7 +177,7 @@ _Source:_ `docs/assets/authority/authority-rate-limit-flow.mmd`
|
||||
## 10. Testing & Tooling
|
||||
- Unit tests: use Mongo2Go (or similar) to exercise credential stores without hitting production infrastructure (`StandardUserCredentialStoreTests` is a template).
|
||||
- Determinism: fix timestamps to UTC and sort outputs consistently; avoid random GUIDs unless stable.
|
||||
- Smoke tests: launch `dotnet run --project src/StellaOps.Authority/StellaOps.Authority` with your plug-in under `PluginBinaries/Authority` and verify `/ready`.
|
||||
- Smoke tests: launch `dotnet run --project src/StellaOps.Authority/StellaOps.Authority` with your plug-in under `StellaOps.Authority.PluginBinaries` and verify `/ready`.
|
||||
- Example verification snippet:
|
||||
```csharp
|
||||
[Fact]
|
||||
@@ -195,7 +195,7 @@ _Source:_ `docs/assets/authority/authority-rate-limit-flow.mmd`
|
||||
|
||||
## 11. Packaging & Delivery
|
||||
- Output assembly should follow `StellaOps.Authority.Plugin.<Name>.dll` so the host’s search pattern picks it up.
|
||||
- Place the compiled DLL plus dependencies under `PluginBinaries/Authority` for offline deployments; include hashes/signatures in release notes (Security Guild guidance forthcoming).
|
||||
- Place the compiled DLL plus dependencies under `StellaOps.Authority.PluginBinaries` for offline deployments; include hashes/signatures in release notes (Security Guild guidance forthcoming).
|
||||
- Document any external prerequisites (e.g., CA cert bundle) in your plug-in README.
|
||||
- Update `etc/authority.plugins/<plugin>.yaml` samples and include deterministic SHA256 hashes for optional bootstrap payloads when distributing Offline Kit artefacts.
|
||||
|
||||
|
||||
86
docs/dev/EXCITITOR_STATEMENT_BACKFILL.md
Normal file
86
docs/dev/EXCITITOR_STATEMENT_BACKFILL.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Excititor Statement Backfill Runbook
|
||||
|
||||
Last updated: 2025-10-19
|
||||
|
||||
## Overview
|
||||
|
||||
Use this runbook when you need to rebuild the `vex.statements` collection from historical raw documents. Typical scenarios:
|
||||
|
||||
- Upgrading the statement schema (e.g., adding severity/KEV/EPSS signals).
|
||||
- Recovering from a partial ingest outage where statements were never persisted.
|
||||
- Seeding a freshly provisioned Excititor deployment from an existing raw archive.
|
||||
|
||||
Backfill operates server-side via the Excititor WebService and reuses the same pipeline that powers the `/excititor/statements` ingestion endpoint. Each raw document is normalized, signed metadata is preserved, and duplicate statements are skipped unless the run is forced.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Connectivity to Excititor WebService** – the CLI uses the backend URL configured in `stellaops.yml` or the `--backend-url` argument.
|
||||
2. **Authority credentials** – the CLI honours the existing Authority client configuration; ensure the caller has permission to invoke admin endpoints.
|
||||
3. **Mongo replica set** (recommended) – causal consistency guarantees rely on majority read/write concerns. Standalone deployment works but skips cross-document transactions.
|
||||
|
||||
## CLI command
|
||||
|
||||
```
|
||||
stellaops excititor backfill-statements \
|
||||
[--retrieved-since <ISO8601>] \
|
||||
[--force] \
|
||||
[--batch-size <int>] \
|
||||
[--max-documents <int>]
|
||||
```
|
||||
|
||||
| Option | Description |
|
||||
| ------ | ----------- |
|
||||
| `--retrieved-since` | Only process raw documents fetched on or after the specified timestamp (UTC by default). |
|
||||
| `--force` | Reprocess documents even if matching statements already exist (useful after schema upgrades). |
|
||||
| `--batch-size` | Number of raw documents pulled per batch (default `100`). |
|
||||
| `--max-documents` | Optional hard limit on the number of raw documents to evaluate. |
|
||||
|
||||
Example – replay the last 48 hours of Red Hat ingest while keeping existing statements:
|
||||
|
||||
```
|
||||
stellaops excititor backfill-statements \
|
||||
--retrieved-since "$(date -u -d '48 hours ago' +%Y-%m-%dT%H:%M:%SZ)"
|
||||
```
|
||||
|
||||
Example – full replay with forced overwrites, capped at 2,000 documents:
|
||||
|
||||
```
|
||||
stellaops excititor backfill-statements --force --max-documents 2000
|
||||
```
|
||||
|
||||
The command returns a summary similar to:
|
||||
|
||||
```
|
||||
Backfill completed: evaluated 450, backfilled 180, claims written 320, skipped 270, failures 0.
|
||||
```
|
||||
|
||||
## Behaviour
|
||||
|
||||
- Raw documents are streamed in ascending `retrievedAt` order.
|
||||
- Each document is normalized using the registered VEX normalizers (CSAF, CycloneDX, OpenVEX).
|
||||
- Statements are appended through the same `IVexClaimStore.AppendAsync` path that powers `/excititor/statements`.
|
||||
- Duplicate detection compares `Document.Digest`; duplicates are skipped unless `--force` is specified.
|
||||
- Failures are logged with the offending digest and continue with the next document.
|
||||
|
||||
## Observability
|
||||
|
||||
- CLI logs aggregate counts and the backend logs per-digest warnings or errors.
|
||||
- Mongo writes carry majority write concern; expect backfill throughput to match ingest baselines (≈5 seconds warm, 30 seconds cold).
|
||||
- Monitor the `excititor.storage.backfill` log scope for detailed telemetry.
|
||||
|
||||
## Post-run verification
|
||||
|
||||
1. Inspect the `vex.statements` collection for the targeted window (check `InsertedAt`).
|
||||
2. Re-run the Excititor storage test suite if possible:
|
||||
```
|
||||
dotnet test src/StellaOps.Excititor.Storage.Mongo.Tests/StellaOps.Excititor.Storage.Mongo.Tests.csproj
|
||||
```
|
||||
3. Optionally, call `/excititor/statements/{vulnerabilityId}/{productKey}` to confirm the expected statements exist.
|
||||
|
||||
## Rollback
|
||||
|
||||
If a forced run produced incorrect statements, use the standard Mongo rollback procedure:
|
||||
|
||||
1. Identify the `InsertedAt` window for the backfill run.
|
||||
2. Delete affected records from `vex.statements` (and any downstream exports if applicable).
|
||||
3. Rerun the backfill command with corrected parameters.
|
||||
@@ -11,6 +11,8 @@
|
||||
- Operator-facing configuration, auditing, and observability.
|
||||
- Out of scope: PoE enforcement (Signer) and CLI/UI client UX; those teams consume the new capabilities.
|
||||
|
||||
> **Status update (2025-10-19):** `ValidateDpopProofHandler`, `AuthorityClientCertificateValidator`, and the supporting storage/audit plumbing now live in `src/StellaOps.Authority`. DPoP proofs populate `cnf.jkt`, mTLS bindings enforce certificate thumbprints via `cnf.x5t#S256`, and token documents persist the sender constraint metadata. In-memory nonce issuance is wired (Redis implementation to follow). Documentation and configuration references were updated (`docs/11_AUTHORITY.md`). Targeted unit/integration tests were added; running the broader test suite is currently blocked by pre-existing `StellaOps.Concelier.Storage.Mongo` build errors.
|
||||
|
||||
## Design Summary
|
||||
- Extract the existing Scanner `DpopProofValidator` stack into a shared `StellaOps.Auth.Security` library used by Authority and resource servers.
|
||||
- Extend Authority configuration (`authority.yaml`) with strongly-typed `senderConstraints.dpop` and `senderConstraints.mtls` sections (map to sample already shown in architecture doc).
|
||||
|
||||
52
docs/dev/authority-plugin-di-coordination.md
Normal file
52
docs/dev/authority-plugin-di-coordination.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Authority Plug-in Scoped Service Coordination
|
||||
|
||||
> Created: 2025-10-19 — Plugin Platform Guild & Authority Core
|
||||
> Status: Scheduled (session confirmed for 2025-10-20 15:00–16:00 UTC)
|
||||
|
||||
This document tracks preparation, agenda, and outcomes for the scoped-service workshop required before implementing PLUGIN-DI-08-002.
|
||||
|
||||
## Objectives
|
||||
|
||||
- Inventory Authority plug-in surfaces that need scoped service lifetimes.
|
||||
- Confirm session/scope handling for identity-provider registrars and background jobs.
|
||||
- Assign follow-up tasks/actions with owners and due dates.
|
||||
|
||||
## Scheduling Snapshot
|
||||
|
||||
- **Meeting time:** 2025-10-20 15:00–16:00 UTC (10:00–11:00 CDT / 08:00–09:00 PDT).
|
||||
- **Facilitator:** Plugin Platform Guild — Alicia Rivera.
|
||||
- **Attendees (confirmed):** Authority Core — Jasmin Patel; Authority Security Guild — Mohan Singh; Plugin Platform — Alicia Rivera, Leah Chen.
|
||||
- **Optional invitees:** DevOps liaison — Sofia Ortega (accepted).
|
||||
- **Logistics:** Invites sent via shared calendar on 2025-10-19 15:30 UTC with Teams bridge + offline dial-in. Meeting notes will be captured here.
|
||||
- **Preparation deadline:** 2025-10-20 12:00 UTC — complete checklist below.
|
||||
|
||||
## Pre-work Checklist
|
||||
|
||||
- Review `ServiceBindingAttribute` contract introduced by PLUGIN-DI-08-001.
|
||||
- Collect existing Authority plug-in registration code paths to evaluate.
|
||||
- Audit background jobs that assume singleton lifetimes.
|
||||
- Identify plug-in health checks/telemetry surfaces impacted by scoped lifetimes.
|
||||
|
||||
### Pre-work References
|
||||
|
||||
- _Add links, file paths, or notes here prior to the session._
|
||||
|
||||
## Draft Agenda
|
||||
|
||||
1. Context recap (5 min) — why scoped DI is needed; summary of PLUGIN-DI-08-001 changes.
|
||||
2. Authority plug-in surfaces (15 min) — registrars, background services, telemetry.
|
||||
3. Session handling strategy (10 min) — scope creation semantics, cancellation propagation.
|
||||
4. Action items & owners (10 min) — capture code/docs/test tasks with due dates.
|
||||
5. Risks & follow-ups (5 min) — dependencies, rollout sequencing.
|
||||
|
||||
## Notes
|
||||
|
||||
- _Pending coordination session; populate afterwards._
|
||||
|
||||
## Action Item Log
|
||||
|
||||
| Item | Owner | Due | Status | Notes |
|
||||
|------|-------|-----|--------|-------|
|
||||
| Confirm meeting time | Alicia Rivera | 2025-10-19 15:30 UTC | DONE | Calendar invite sent; all required attendees accepted |
|
||||
| Compile Authority plug-in DI entry points | Jasmin Patel | 2025-10-20 | IN PROGRESS | Gather current Authority plug-in registrars, background jobs, and helper factories that assume singleton lifetimes; add the list with file paths to **Pre-work References** in this document before 2025-10-20 12:00 UTC. |
|
||||
| Outline scoped-session pattern for background jobs | Leah Chen | Post-session | BLOCKED | Requires meeting outcomes |
|
||||
@@ -19,19 +19,19 @@ This dashboard tracks connector readiness for emitting `AffectedPackage.Normaliz
|
||||
| Connector | Owner team | Normalized versions status | Last update | Next action / link |
|
||||
|-----------|------------|---------------------------|-------------|--------------------|
|
||||
| Acsc | BE-Conn-ACSC | ❌ Not started – mapper pending | 2025-10-11 | Design DTOs + mapper with normalized rule array; see `src/StellaOps.Concelier.Connector.Acsc/TASKS.md`. |
|
||||
| Cccs | BE-Conn-CCCS | ❌ Not started – mapper pending | 2025-10-11 | Add normalized SemVer array in canonical mapper; coordinate fixtures per `TASKS.md`. |
|
||||
| CertBund | BE-Conn-CERTBUND | ✅ Canonical mapper emitting vendor ranges | 2025-10-14 | Normalized vendor range payloads landed alongside telemetry/docs updates; see `src/StellaOps.Concelier.Connector.CertBund/TASKS.md`. |
|
||||
| Cccs | BE-Conn-CCCS | ⚠️ Scheduled – helper ready, implementation due 2025-10-21 | 2025-10-19 | Apply Merge-provided trailing-version helper to emit `NormalizedVersions`; update mapper/tests per `src/StellaOps.Concelier.Connector.Cccs/TASKS.md`. |
|
||||
| CertBund | BE-Conn-CERTBUND | ⚠️ Follow-up – translate `versions` strings to normalized rules | 2025-10-19 | Build `bis`/`alle` translator + fixtures before 2025-10-22 per `src/StellaOps.Concelier.Connector.CertBund/TASKS.md`. |
|
||||
| CertCc | BE-Conn-CERTCC | ⚠️ In progress – fetch pipeline DOING | 2025-10-11 | Implement VINCE mapper with SemVer/NEVRA rules; unblock snapshot regeneration; `src/StellaOps.Concelier.Connector.CertCc/TASKS.md`. |
|
||||
| Kev | BE-Conn-KEV | ✅ Normalized catalog/due-date rules verified | 2025-10-12 | Fixtures reconfirmed via `dotnet test src/StellaOps.Concelier.Connector.Kev.Tests`; `src/StellaOps.Concelier.Connector.Kev/TASKS.md`. |
|
||||
| Cve | BE-Conn-CVE | ✅ Normalized SemVer rules verified | 2025-10-12 | Snapshot parity green (`dotnet test src/StellaOps.Concelier.Connector.Cve.Tests`); `src/StellaOps.Concelier.Connector.Cve/TASKS.md`. |
|
||||
| Ghsa | BE-Conn-GHSA | ⚠️ DOING – normalized rollout task active | 2025-10-11 18:45 UTC | Wire `SemVerRangeRuleBuilder` + refresh fixtures; `src/StellaOps.Concelier.Connector.Ghsa/TASKS.md`. |
|
||||
| Osv | BE-Conn-OSV | ✅ SemVer mapper & parity fixtures verified | 2025-10-12 | GHSA parity regression passing (`dotnet test src/StellaOps.Concelier.Connector.Osv.Tests`); `src/StellaOps.Concelier.Connector.Osv/TASKS.md`. |
|
||||
| Ics.Cisa | BE-Conn-ICS-CISA | ❌ Not started – mapper TODO | 2025-10-11 | Plan SemVer/firmware scheme selection; `src/StellaOps.Concelier.Connector.Ics.Cisa/TASKS.md`. |
|
||||
| Kisa | BE-Conn-KISA | ✅ Landed 2025-10-14 (mapper + telemetry) | 2025-10-11 | Hangul-aware mapper emits normalized rules; see `docs/dev/kisa_connector_notes.md` for localisation/metric details. |
|
||||
| Ics.Cisa | BE-Conn-ICS-CISA | ⚠️ Decision pending – normalize SemVer exacts or escalate scheme | 2025-10-19 | Promote `SemVerPrimitive` outputs into `NormalizedVersions` or file Models ticket by 2025-10-23 (`src/StellaOps.Concelier.Connector.Ics.Cisa/TASKS.md`). |
|
||||
| Kisa | BE-Conn-KISA | ⚠️ Proposal required – firmware scheme due 2025-10-24 | 2025-10-19 | Draft `kisa.build` (or equivalent) scheme with Models, then emit normalized rules; track in `src/StellaOps.Concelier.Connector.Kisa/TASKS.md`. |
|
||||
| Ru.Bdu | BE-Conn-BDU | ✅ Raw scheme emitted | 2025-10-14 | Mapper now writes `ru-bdu.raw` normalized rules with provenance + telemetry; `src/StellaOps.Concelier.Connector.Ru.Bdu/TASKS.md`. |
|
||||
| Ru.Nkcki | BE-Conn-Nkcki | ❌ Not started – mapper TODO | 2025-10-11 | Similar to BDU; ensure Cyrillic provenance preserved; `src/StellaOps.Concelier.Connector.Ru.Nkcki/TASKS.md`. |
|
||||
| Vndr.Apple | BE-Conn-Apple | ✅ Shipped – emitting normalized arrays | 2025-10-11 | Continue fixture/tooling work; `src/StellaOps.Concelier.Connector.Vndr.Apple/TASKS.md`. |
|
||||
| Vndr.Cisco | BE-Conn-Cisco | ✅ SemVer + vendor extensions emitted | 2025-10-14 | Connector outputs SemVer primitives with `cisco.productId` notes; see `CiscoMapper` and fixtures for coverage. |
|
||||
| Vndr.Cisco | BE-Conn-Cisco | ⚠️ Scheduled – normalized rule emission due 2025-10-21 | 2025-10-19 | Use Merge helper to persist `NormalizedVersions` alongside SemVer primitives; see `src/StellaOps.Concelier.Connector.Vndr.Cisco/TASKS.md`. |
|
||||
| Vndr.Msrc | BE-Conn-MSRC | ✅ Map + normalized build rules landed | 2025-10-15 | `MsrcMapper` emits `msrc.build` normalized rules with CVRF references; see `src/StellaOps.Concelier.Connector.Vndr.Msrc/TASKS.md`. |
|
||||
| Nvd | BE-Conn-NVD | ⚠️ Needs follow-up – mapper complete but normalized array MR pending | 2025-10-11 | Align CVE notes + normalized payload flag; `src/StellaOps.Concelier.Connector.Nvd/TASKS.md`. |
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"deliveryId": "delivery-7f3b6c51",
|
||||
"tenantId": "tenant-acme",
|
||||
"ruleId": "rule-critical-slack",
|
||||
"actionId": "slack-secops",
|
||||
"eventId": "4f6e9c09-01b4-4c2a-8a57-3d06de182d74",
|
||||
"kind": "scanner.report.ready",
|
||||
"status": "Sent",
|
||||
"statusReason": null,
|
||||
"rendered": {
|
||||
"channelType": "Slack",
|
||||
"format": "Slack",
|
||||
"target": "#sec-alerts",
|
||||
"title": "Critical findings detected",
|
||||
"body": "{\"text\":\"Critical findings detected\",\"blocks\":[{\"type\":\"section\",\"text\":{\"type\":\"mrkdwn\",\"text\":\"*Critical findings detected*\\n1 new critical finding across 2 images.\"}},{\"type\":\"context\",\"elements\":[{\"type\":\"mrkdwn\",\"text\":\"Preview generated 2025-10-19T16:23:41.889Z · Trace `trace-58c212`\"}]}]}",
|
||||
"summary": "1 new critical finding across 2 images.",
|
||||
"textBody": "1 new critical finding across 2 images.\nTrace: trace-58c212",
|
||||
"locale": "en-us",
|
||||
"bodyHash": "febf9b2a630d862b07f4390edfbf31f5e8b836529f5232c491f4b3f6dba4a4b2",
|
||||
"attachments": []
|
||||
},
|
||||
"attempts": [
|
||||
{
|
||||
"timestamp": "2025-10-19T16:23:42.112Z",
|
||||
"status": "Succeeded",
|
||||
"statusCode": 200,
|
||||
"reason": null
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"channelType": "slack",
|
||||
"target": "#sec-alerts",
|
||||
"previewProvider": "fallback",
|
||||
"traceId": "trace-58c212",
|
||||
"slack.channel": "#sec-alerts"
|
||||
},
|
||||
"createdAt": "2025-10-19T16:23:41.889Z",
|
||||
"sentAt": "2025-10-19T16:23:42.101Z",
|
||||
"completedAt": "2025-10-19T16:23:42.112Z"
|
||||
}
|
||||
],
|
||||
"count": 1,
|
||||
"continuationToken": "2025-10-19T16:23:41.889Z|tenant-acme:delivery-7f3b6c51"
|
||||
}
|
||||
196
docs/ops/concelier-mirror-operations.md
Normal file
196
docs/ops/concelier-mirror-operations.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# Concelier & Excititor Mirror Operations
|
||||
|
||||
This runbook describes how Stella Ops operates the managed mirrors under `*.stella-ops.org`.
|
||||
It covers Docker Compose and Helm deployment overlays, secret handling for multi-tenant
|
||||
authn, CDN fronting, and the recurring sync pipeline that keeps mirror bundles current.
|
||||
|
||||
## 1. Prerequisites
|
||||
|
||||
- **Authority access** – client credentials (`client_id` + secret) authorised for
|
||||
`concelier.mirror.read` and `excititor.mirror.read` scopes. Secrets live outside git.
|
||||
- **Signed TLS certificates** – wildcard or per-domain (`mirror-primary`, `mirror-community`).
|
||||
Store them under `deploy/compose/mirror-gateway/tls/` or in Kubernetes secrets.
|
||||
- **Mirror gateway credentials** – Basic Auth htpasswd files per domain. Generate with
|
||||
`htpasswd -B`. Operators distribute credentials to downstream consumers.
|
||||
- **Export artifact source** – read access to the canonical S3 buckets (or rsync share)
|
||||
that hold `concelier` JSON bundles and `excititor` VEX exports.
|
||||
- **Persistent volumes** – storage for Concelier job metadata and mirror export trees.
|
||||
For Helm, provision PVCs (`concelier-mirror-jobs`, `concelier-mirror-exports`,
|
||||
`excititor-mirror-exports`, `mirror-mongo-data`, `mirror-minio-data`) before rollout.
|
||||
|
||||
## 2. Secret & certificate layout
|
||||
|
||||
### Docker Compose (`deploy/compose/docker-compose.mirror.yaml`)
|
||||
|
||||
- `deploy/compose/env/mirror.env.example` – copy to `.env` and adjust quotas or domain IDs.
|
||||
- `deploy/compose/mirror-secrets/` – mount read-only into `/run/secrets`. Place:
|
||||
- `concelier-authority-client` – Authority client secret.
|
||||
- `excititor-authority-client` (optional) – reserve for future authn.
|
||||
- `deploy/compose/mirror-gateway/tls/` – PEM-encoded cert/key pairs:
|
||||
- `mirror-primary.crt`, `mirror-primary.key`
|
||||
- `mirror-community.crt`, `mirror-community.key`
|
||||
- `deploy/compose/mirror-gateway/secrets/` – htpasswd files:
|
||||
- `mirror-primary.htpasswd`
|
||||
- `mirror-community.htpasswd`
|
||||
|
||||
### Helm (`deploy/helm/stellaops/values-mirror.yaml`)
|
||||
|
||||
Create secrets in the target namespace:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic concelier-mirror-auth \
|
||||
--from-file=concelier-authority-client=concelier-authority-client
|
||||
|
||||
kubectl create secret generic excititor-mirror-auth \
|
||||
--from-file=excititor-authority-client=excititor-authority-client
|
||||
|
||||
kubectl create secret tls mirror-gateway-tls \
|
||||
--cert=mirror-primary.crt --key=mirror-primary.key
|
||||
|
||||
kubectl create secret generic mirror-gateway-htpasswd \
|
||||
--from-file=mirror-primary.htpasswd --from-file=mirror-community.htpasswd
|
||||
```
|
||||
|
||||
> Keep Basic Auth lists short-lived (rotate quarterly) and document credential recipients.
|
||||
|
||||
## 3. Deployment
|
||||
|
||||
### 3.1 Docker Compose (edge mirrors, lab validation)
|
||||
|
||||
1. `cp deploy/compose/env/mirror.env.example deploy/compose/env/mirror.env`
|
||||
2. Populate secrets/tls directories as described above.
|
||||
3. Sync mirror bundles (see §4) into `deploy/compose/mirror-data/…` and ensure they are mounted
|
||||
on the host path backing the `concelier-exports` and `excititor-exports` volumes.
|
||||
4. Run the profile validator: `deploy/tools/validate-profiles.sh`.
|
||||
5. Launch: `docker compose --env-file env/mirror.env -f docker-compose.mirror.yaml up -d`.
|
||||
|
||||
### 3.2 Helm (production mirrors)
|
||||
|
||||
1. Provision PVCs sized for mirror bundles (baseline: 20 GiB per domain).
|
||||
2. Create secrets/tls config maps (§2).
|
||||
3. `helm upgrade --install mirror deploy/helm/stellaops -f deploy/helm/stellaops/values-mirror.yaml`.
|
||||
4. Annotate the `stellaops-mirror-gateway` service with ingress/LoadBalancer metadata required by
|
||||
your CDN (e.g., AWS load balancer scheme internal + NLB idle timeout).
|
||||
|
||||
## 4. Artifact sync workflow
|
||||
|
||||
Mirrors never generate exports—they ingest signed bundles produced by the Concelier and Excititor
|
||||
export jobs. Recommended sync pattern:
|
||||
|
||||
### 4.1 Compose host (systemd timer)
|
||||
|
||||
`/usr/local/bin/mirror-sync.sh`:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
export AWS_ACCESS_KEY_ID=…
|
||||
export AWS_SECRET_ACCESS_KEY=…
|
||||
|
||||
aws s3 sync s3://mirror-stellaops/concelier/latest \
|
||||
/opt/stellaops/mirror-data/concelier --delete --size-only
|
||||
|
||||
aws s3 sync s3://mirror-stellaops/excititor/latest \
|
||||
/opt/stellaops/mirror-data/excititor --delete --size-only
|
||||
```
|
||||
|
||||
Schedule with a systemd timer every 5 minutes. The Compose volumes mount `/opt/stellaops/mirror-data/*`
|
||||
into the containers read-only, matching `CONCELIER__MIRROR__EXPORTROOT=/exports/json` and
|
||||
`EXCITITOR__ARTIFACTS__FILESYSTEM__ROOT=/exports`.
|
||||
|
||||
### 4.2 Kubernetes (CronJob)
|
||||
|
||||
Create a CronJob running the AWS CLI (or rclone) in the same namespace, writing into the PVCs:
|
||||
|
||||
```yaml
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: mirror-sync
|
||||
spec:
|
||||
schedule: "*/5 * * * *"
|
||||
jobTemplate:
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: sync
|
||||
image: public.ecr.aws/aws-cli/aws-cli@sha256:5df5f52c29f5e3ba46d0ad9e0e3afc98701c4a0f879400b4c5f80d943b5fadea
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- >
|
||||
aws s3 sync s3://mirror-stellaops/concelier/latest /exports/concelier --delete --size-only &&
|
||||
aws s3 sync s3://mirror-stellaops/excititor/latest /exports/excititor --delete --size-only
|
||||
volumeMounts:
|
||||
- name: concelier-exports
|
||||
mountPath: /exports/concelier
|
||||
- name: excititor-exports
|
||||
mountPath: /exports/excititor
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: mirror-sync-aws
|
||||
restartPolicy: OnFailure
|
||||
volumes:
|
||||
- name: concelier-exports
|
||||
persistentVolumeClaim:
|
||||
claimName: concelier-mirror-exports
|
||||
- name: excititor-exports
|
||||
persistentVolumeClaim:
|
||||
claimName: excititor-mirror-exports
|
||||
```
|
||||
|
||||
## 5. CDN integration
|
||||
|
||||
1. Point the CDN origin at the mirror gateway (Compose host or Kubernetes LoadBalancer).
|
||||
2. Honour the response headers emitted by the gateway and Concelier/Excititor:
|
||||
`Cache-Control: public, max-age=300, immutable` for mirror payloads.
|
||||
3. Configure origin shields in the CDN to prevent cache stampedes. Recommended TTLs:
|
||||
- Index (`/concelier/exports/index.json`, `/excititor/mirror/*/index`) → 60 s.
|
||||
- Bundle/manifest payloads → 300 s.
|
||||
4. Forward the `Authorization` header—Basic Auth terminates at the gateway.
|
||||
5. Enforce per-domain rate limits at the CDN (matching gateway budgets) and enable logging
|
||||
to SIEM for anomaly detection.
|
||||
|
||||
## 6. Smoke tests
|
||||
|
||||
After each deployment or sync cycle:
|
||||
|
||||
```bash
|
||||
# Index with Basic Auth
|
||||
curl -u $PRIMARY_CREDS https://mirror-primary.stella-ops.org/concelier/exports/index.json | jq 'keys'
|
||||
|
||||
# Mirror manifest signature
|
||||
curl -u $PRIMARY_CREDS -I https://mirror-primary.stella-ops.org/concelier/exports/mirror/primary/manifest.json
|
||||
|
||||
# Excititor consensus bundle metadata
|
||||
curl -u $COMMUNITY_CREDS https://mirror-community.stella-ops.org/excititor/mirror/community/index \
|
||||
| jq '.exports[].exportKey'
|
||||
|
||||
# Signed bundle + detached JWS (spot check digests)
|
||||
curl -u $PRIMARY_CREDS https://mirror-primary.stella-ops.org/concelier/exports/mirror/primary/bundle.json.jws \
|
||||
-o bundle.json.jws
|
||||
cosign verify-blob --signature bundle.json.jws --key mirror-key.pub bundle.json
|
||||
```
|
||||
|
||||
Watch the gateway metrics (`nginx_vts` or access logs) for cache hits. In Kubernetes, `kubectl logs deploy/stellaops-mirror-gateway`
|
||||
should show `X-Cache-Status: HIT/MISS`.
|
||||
|
||||
## 7. Maintenance & rotation
|
||||
|
||||
- **Bundle freshness** – alert if sync job lag exceeds 15 minutes or if `concelier` logs
|
||||
`Mirror export root is not configured`.
|
||||
- **Secret rotation** – change Authority client secrets and Basic Auth credentials quarterly.
|
||||
Update the mounted secrets and restart deployments (`docker compose restart concelier` or
|
||||
`kubectl rollout restart deploy/stellaops-concelier`).
|
||||
- **TLS renewal** – reissue certificates, place new files, and reload gateway (`docker compose exec mirror-gateway nginx -s reload`).
|
||||
- **Quota tuning** – adjust per-domain `MAXDOWNLOADREQUESTSPERHOUR` in `.env` or values file.
|
||||
Align CDN rate limits and inform downstreams.
|
||||
|
||||
## 8. References
|
||||
|
||||
- Deployment profiles: `deploy/compose/docker-compose.mirror.yaml`,
|
||||
`deploy/helm/stellaops/values-mirror.yaml`
|
||||
- Mirror architecture dossiers: `docs/ARCHITECTURE_CONCELIER.md`,
|
||||
`docs/ARCHITECTURE_EXCITITOR_MIRRORS.md`
|
||||
- Export bundling: `docs/ARCHITECTURE_DEVOPS.md` §3, `docs/ARCHITECTURE_EXCITITOR.md` §7
|
||||
Reference in New Issue
Block a user