Surface.FS Design (Epic: SURFACE-SHARING)

Status: Draft v1.1 — aligns with tasks SURFACE-FS-01..06, SCANNER-SURFACE-01..05, ZASTAVA-SURFACE-01..02, SCHED-SURFACE-01, OPS-SECRETS-01..02.

Audience: Scanner Worker/WebService, Zastava, Scheduler, DevOps. Component map: See Scanner architecture — §1 System landscape for end-to-end placement.

1. Purpose

Surface.FS provides a unified content-addressable cache for Scanner-derived artefacts (layer manifests, entry traces, SBOM fragments, runtime deltas). It enables:

2. Core Concepts

2.1 Artefact Key

Each artefact is addressed by a tuple (tenant, surfaceKind, contentDigest) where contentDigest is a SHA256 of the canonical payload. surfaceKind identifies artefact type (see Manifest schema below).

2.2 Manifest

Manifests describe the artefact metadata and storage pointers. They are stored in the surface-manifests bucket and fetched by consumers before retrieving bulk data.

{
  "schema": "stellaops.surface.manifest@1",
  "tenant": "acme",
  "imageDigest": "sha256:cafe...",
  "scanId": "scan-1234",
  "generatedAt": "2025-10-29T12:00:00Z",
  "source": {
    "component": "scanner.worker",
    "version": "2025.10.0",
    "workerInstance": "scanner-worker-1",
    "attempt": 1
  },
  "artifacts": [
    {
      "kind": "entrytrace.graph",
      "uri": "cas://surface-cache/manifests/acme/ab/cd/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789.json",
      "digest": "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
      "mediaType": "application/vnd.stellaops.entrytrace+json",
      "format": "json",
      "sizeBytes": 524288,
      "view": "runtime",
      "attestations": [
        {
          "kind": "dsse",
          "mediaType": "application/vnd.dsse+json",
          "digest": "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
          "uri": "cas://surface-cache/attestations/entrytrace.graph.dsse/e3b0c442....json"
        }
      ],
      "storage": {
        "bucket": "surface-cache",
        "objectKey": "payloads/acme/entrytrace/sha256/ab/cd/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789.ndjson.zst",
        "sizeBytes": 524288,
        "contentType": "application/x-ndjson+zstd"
      },
      "metadata": {
        "entrypoint": "/usr/bin/java",
        "surfaceVersion": "1"
      }
    }
  ]
}

Manifest URIs follow the deterministic pattern:

cas://{bucket}/{prefix}/{tenant}/{digest[0..1]}/{digest[2..3]}/{digest}.json

The hex portion of the manifest digest is split into two directory levels to avoid hot directories. The same layout is mirrored on disk by the default FileSurfaceManifestStore, which keeps offline bundle sync trivial (copy the manifests/ tree verbatim).

Deterministic composition adds:

2.3 Payload Storage

Large payloads (SBOM fragments, entry traces, runtime events) live in the same object store as manifests (RustFS/S3). Manifests record relative paths so offline bundles can copy both manifest and payload without modification.

3. APIs

Surface.FS exposes .NET-first abstractions that hosts consume via DI:

All components honour configuration bound from Surface:Cache and Surface:Manifest (or environment mirrors like SCANNER_SURFACE_CACHE_ROOT). SurfaceManifestStoreOptions controls the URI scheme/bucket/prefix and allows overriding the manifest directory while still defaulting to <cacheRoot>/manifests.

WebService integration (2025-11-05)

4. Library Responsibilities

Surface.FS library for .NET hosts provides:

5. Retention & Eviction

6. Offline Kit Handling

Offline kits include:

offline/surface/
  manifests/
    <tenant>/<digest[0..1]>/<digest[2..3]>/<digest>.json
  payloads/
    <tenant>/<kind>/<digest[0..1]>/<digest[2..3]>/<digest>.json.zst
  manifest-index.json

Import script uses ISurfaceManifestWriter.PublishAsync for each manifest after verifying the embedded digest, keeping Offline Kit replays identical to online flows. This enables Zastava and Scheduler running offline to consume cached data without re-scanning.

6.1 EntryTrace Cache Usage

Scanner.Worker serialises EntryTrace graphs into Surface.FS using SurfaceCacheKey(namespace: "entrytrace.graph", tenant, sha256(options|env|entrypoint)). At runtime the worker checks the cache before invoking analyzers; cache hits bypass parsing and feed the result store/attestor pipeline directly. The same namespace is consumed by WebService and CLI to retrieve cached graphs for reporting.

6.2 BuildX generator path

StellaOps.Scanner.Sbomer.BuildXPlugin reuses the same CAS layout via the --surface-* descriptor flags (or STELLAOPS_SURFACE_* env vars). When layer fragment JSON, EntryTrace graph JSON, or NDJSON files are supplied, the plug-in writes them under scanner/surface/** within the configured CAS root and emits a manifest pointer so Scanner.WebService can pick up the artefacts without re-scanning. The Surface manifest JSON can also be copied to an arbitrary path via --surface-manifest-output for CI artefacts/offline kits.

7. Security & Tenancy

8. Observability

9. Testing Strategy

10. Future Enhancements

11. References