Skip to content
Docs/Architecture/Time Dimension Segmentation
Edit on GitHub

Time Dimension Segmentation

Time Dimension Segmentation (TDS) is a cross-cutting architectural principle for all opensecstack platforms. It assigns every API endpoint and background job to one of three named latency tiers, prescribes the implementation pattern for each tier, and provides a tool — tds-scanner — to measure compliance at runtime. TDS is defined across the full Architecture and applies to every platform in the ecosystem.

The rationale and formal decision record are in ADR-009.

What is TDS?

opensecstack platforms handle operations whose latency requirements span many orders of magnitude — from sub-millisecond hash computations to multi-minute full-platform audit scans. Without explicit latency contracts, teams repeatedly made the same mistakes: synchronous endpoints blocking on operations that should have been async, fast operations delayed by co-located slow work, and no shared vocabulary for discussing expected response times across codebases written in Go, Rust, Python, and TypeScript.

TDS solves this by dividing operations into three tiers named after clock hands. Each tier carries an implicit latency contract and a prescribed implementation pattern. Saying "this endpoint is second-hand" communicates both the bound (<300ms) and the required pattern (synchronous HTTP response) to anyone who has read this page once.

The three tiers

TierLatency boundImplementation patternExample operations
Second hand<300msSynchronous HTTP responseHealth check, status poll, per-endpoint CVSS scoring, MARSHAL gate evaluation, AUGUR advisory fetch, control status update
Minute hand300ms – 30sShort-lived async job with polling endpoint, or synchronous with generous timeout (acceptable for operations <10s)HTML/PDF report generation, full scan of a small API spec (<50 endpoints), evidence artifact upload, audit log fetch, chain anchor age check
Hour hand>30sBackground job with webhook callback or polling endpoint — never synchronousFull scan of a large API spec (>200 endpoints), full compliance export, WORM chain verification (7-day window), VIGIL_DEEP full audit scan
Rule: Any hour-hand operation inside a synchronous HTTP handler is automatically a design violation. The trigger must be second-hand; the slow work must run in the background.

Prometheus alerting rules fire when a second-hand operation's P95 latency exceeds 250ms (warning) or 300ms (critical). All new API endpoints and background jobs must declare their TDS tier in the design document or ADR before a pull request can be merged.

Platform TDS assignments

APIGuard

ComponentTier
Spec parse (Rust subprocess)Second hand
Per-endpoint analysis (Go)Second hand
CVSS scoringSecond hand
Scan status APISecond hand
HTML report generationMinute hand
PDF report generationMinute hand
Full scan — small spec (<50 endpoints)Minute hand
Full scan — large spec (>200 endpoints)Hour hand

NIS2 Compass

ComponentTier
Control status updateSecond hand
Organisation CRUDSecond hand
Evidence artifact uploadMinute hand
Audit log fetchMinute hand
Full compliance exportHour hand

CITADEL

ComponentTier
MARSHAL gate evaluationSecond hand
AUGUR advisory fetchSecond hand
VIGIL_REALTIME status pollSecond hand
Chain anchor age checkMinute hand
WORM chain verify (7-day window)Hour hand
VIGIL_DEEP full audit scanHour hand

Implementation patterns

Second-hand — synchronous handler

Must complete at P95 within 300ms. Avoid external HTTP calls (use cached data), full table scans without indexes, subprocess invocations longer than ~50ms, and loops over unbounded datasets.

Go
"cm">// TDS: second-hand
func (h *Handler) GetVIGILStatus(w http.ResponseWriter, r *http.Request) {
    status, err := h.vigil.CurrentStatus(r.Context())
    if err != nil {
        http.Error(w, err.Error(), 500)
        return
    }
    json.NewEncoder(w).Encode(status)
}

Minute-hand — async job with polling

Go
"cm">// TDS: minute-hand — Pattern B: async job with polling
"cm">// POST /api/v1/reports — starts job, returns job ID immediately
func (h *Handler) StartReport(w http.ResponseWriter, r *http.Request) {
    jobID := h.jobs.Enqueue(reportJob)
    w.WriteHeader(http.StatusAccepted)
    json.NewEncoder(w).Encode(map[string]string{"job_id": jobID})
}

"cm">// GET /api/v1/reports/{job_id} — poll for result
func (h *Handler) GetReport(w http.ResponseWriter, r *http.Request) {
    job, _ := h.jobs.Get(jobID)
    if job.Status == "pending" {
        w.WriteHeader(http.StatusAccepted)
        return
    }
    json.NewEncoder(w).Encode(job.Result)
}

Hour-hand — background job, never synchronous

Go
"cm">// TDS: hour-hand
"cm">// POST /api/v1/vigil/deep-scan — enqueues scan, returns immediately
func (h *Handler) TriggerDeepScan(w http.ResponseWriter, r *http.Request) {
    scanID := h.deepScanner.Enqueue(DeepScanJob{Period: period})
    w.WriteHeader(http.StatusAccepted)
    json.NewEncoder(w).Encode(map[string]string{
        "scan_id":    scanID,
        "poll_url":   fmt.Sprintf("/api/v1/vigil/deep-scan/%s", scanID),
        "webhook_on": "vigil_deep_completed",
    })
}

Cross-tier decomposition

Some operations have a fast trigger but slow execution. Decompose them so the caller always gets a fast response and the slow work runs in the background:

Bash
"cm"># Second-hand:  POST /api/v1/scans          → {scan_id, status: "queued"}
"cm"># Second-hand:  GET  /api/v1/scans/{id}     → current status and progress
"cm"># Hour-hand:    [background] scan execution → updates status as it runs
"cm"># Second-hand:  GET  /api/v1/scans/{id}/findings → findings once complete

TripleHash alignment

The CITADEL TripleHash scheme (BLAKE3 + SHA-256 + SHA-512) maps directly to TDS tiers, making the relationship between cryptographic assurance level and operational latency explicit:

Hash algorithmTDS tierUse
BLAKE3Second handPer-request real-time integrity checks
SHA-256Minute handWORM chain hashing
SHA-512Hour handLong-term archival and Ed25519 anchor signing
Go
hash := vantage_hash.TripleHash.Compute(content)

"cm">// Second-hand: real-time integrity
realtimeCheck := hash.Blake3Hex()

"cm">// Minute-hand: WORM chain hashing
chainHash := hash.SHA256Hex()

"cm">// Hour-hand: archival / anchor signing
archivalHash := hash.SHA512Hex()

tds-scanner

tds-scanner is a CLI tool in sdk/tools/tds-scanner/ that measures actual operation latencies against tier bounds. It is the standard way to verify TDS compliance for a running deployment.

Installation

Bash
go install github.com/opensecstack/sdk/tools/tds-scanner@latest

"cm"># Or build from source
cd sdk/tools/tds-scanner
go build -o tds-scanner .

Running a scan

Bash
tds-scanner scan \
  --target https://your-platform.internal \
  --api-key $KEY \
  --platform apiguard    "cm"># apiguard | nis2compass | citadel
  --iterations 5         "cm"># median of 5 runs per operation (default)
  --output text          "cm"># text | json | junit

Key flags

FlagDefaultDescription
--target—Platform base URL (required)
--api-key—API key for the platform (required)
--platformapiguardWhich platform's operation set to test
--iterations5Runs per operation; median is reported
--outputtextOutput format: text, json, or junit
--fail-on-violationfalseExit code 1 if any TDS violation is found
--timeout300sMaximum total scan duration

A TDS violation does not mean the system is broken — it means an operation is taking longer than its tier contract allows. Common causes for second-hand violations are missing database indexes, network latency between scanner and platform, or the platform running under load.

JSON output format

TypeScript
{
  "scan_id": "uuid",
  "ts_utc": "2026-03-30T14:00:00Z",
  "platform": "apiguard",
  "target": "https:class=class="str">"cm">//apiguard.internal",
  "operations": [
    {
      "name": "scan_start",
      "tier": "second-hand",
      "tier_bound_ms": 300,
      "median_ms": 87,
      "p95_ms": 102,
      "status": "PASS"
    }
  ],
  "summary": {
    "total": 9,
    "pass": 9,
    "fail": 0,
    "tds_compliant": true
  }
}

CI/CD integration

Run tds-scanner in CI with --fail-on-violation to prevent TDS regressions from reaching production. The junit output format is compatible with GitHub Actions, GitLab CI, and Jenkins.

.github/workflows/tds.yml
YAML
- name: TDS compliance scan
  run: |
    tds-scanner scan \
      --target ${{ secrets.APIGUARD_URL }} \
      --api-key ${{ secrets.APIGUARD_KEY }} \
      --output junit \
      --fail-on-violation \
      > tds-results.xml

- name: Publish TDS results
  uses: mikepenz/action-junit-report@v4
  with: report_paths: tds-results.xml

To capture a latency baseline for regression comparison use tds-scanner baseline and pass --compare-baseline baseline.json on subsequent scans.

SIN — Security Intelligence Network — Open-source cybersecurity & compliance for the EU Digital Decade.

Apache-2.0 & AGPL-3.0 · GitHub · Contribute