# network-synapix — complete library (all audiences)

_Generated whole-library download. See `agent-manifest.json` → `library` for discovery._

## Table of contents

- **Overview** (`00-overview`, builders)
- **Overview** (`00-overview`, developers)
- **Client API** (`01-client-api`, builders)
- **Paths and exposure** (`02-paths-and-exposure`, builders)
- **Known unknowns** (`03-known-unknowns`, builders)
- **SEAP and narratives** (`04-seap-and-narratives`, builders)
- **Ops and lab pipeline** (`05-ops-lab`, developers)

---

# Overview — builders

# Overview — Client integrators

**Audience:** UI and product engineers integrating the Security Map.  
**Related:** [Client API](../01-client-api/builders/BOOK.md) · [Paths and exposure](../02-paths-and-exposure/builders/BOOK.md)

---

## 1. What network-synapix is

A **network-synapix** is the immutable **network estate bundle** your client session reads. It is identified by **`snapshotSetId`** (for example `scs:17d3e1716f4812085529c89f`).

It bundles:

- Level-1 evidence compiled from **connectors** (CrowdStrike, FortiGate, Splunk, …)
- SCG snapshot-set membership and optional identity federation
- **Finding-context** (placement, path status, exposure summaries)
- Sidecar **SEAP analysis**, **narratives**, and **framework** refs advertised on bootstrap

Clients **never** follow family `current` or `latest`. Always **`GET …/bootstrap`**, then send that `snapshotSetId` on every query.

---

## 2. Terminology and API fields

| Say (prose) | API field | Example |
|-------------|-----------|---------|
| network-synapix | `snapshotSetId` | `scs:…` |
| network-synapix id | same | on every Map/SEAP body |
| Analysis snapshot | `analysisSnapshotId` | `seap-…` |
| Finding-context version | `contextAnalysisVersion` / `fcv:…` | header on finding-context |
| Narrative snapshot | `narrativeSnapshotId` | bootstrap overlay |

Legacy terms **pin**, **Map pin**, and bare **Synapix** are deprecated in user-facing text.

Internal code may still use `mapPinId` until a dedicated rename — contracts are unchanged.

---

## 3. Architecture stack

```text
Connectors (vendor acquisition)
        ↓
Map-1 abstracts → Syncpix Level-1 compile (NRTS / SOES / SAES / …)
        ↓
SCG snapshot-set → network-synapix (snapshotSetId)
        ↓
Map BFF  :9222  /security-map/v1     ← client read API
        ↓
SEAP     :7540  /security-analysis/v1 ← analysis sidecar
Investigator :7541 (evidence-only IP reports)
```

**Client API = Map BFF + SEAP only.** Do not call connectors, live-tester, or Syncpix skill ports (`:7520`, `:7522`, `:7524`) from the UI.

---

## 4. Do and do not

| Do | Do not |
|----|--------|
| Bootstrap `snapshotSetId` each session | Follow family `current` |
| Paint **path unknown** when `pathStatus` says so | Collapse to “no path” or “isolated” |
| Treat possible paths as **candidate** placement | Treat as confirmed attack path |
| Show **known unknowns** as first-class | Silent nulls or “all clear” |
| Query SEAP for scores and joins | Stamp analysis onto Map-1 graph nodes |

Re-bootstrap when bootstrap ids look stale (wrong federation, undated bags, old Word exports).

---

# Overview — developers

# Overview — Operators

**Audience:** Engineers refreshing estates and running SEAP in lab.  
**Related:** [Ops and lab pipeline](../05-ops-lab/developers/BOOK.md)

Same content as the client overview — operators publish network-synapix ids; clients consume them.

See [00-overview/builders/BOOK.md](../builders/BOOK.md) for terminology and architecture. Your write plane is connectors + Syncpix compile + `live:analyze`, not the Map BFF query surface.

---

# Client API — builders

# Client API — Client integrators

**Audience:** Engineers wiring the Security Map read plane.  
**Base:** `http://127.0.0.1:9222/security-map/v1` · Header: `x-tenant-id: default` · Media type: `application/vnd.security-map.v1+json`

---

## 1. Bootstrap and session rules

```http
GET /security-map/v1/tenants/default/bootstrap
```

Read **`snapshotSetId`**, **`analysisSnapshotId`**, **`narrativeSnapshotId`**, **`understandingProfileId`**, capability flags (`attackPathCapability`, `possiblePathsCapability`), and pinned family snapshot refs.

Rules:

1. One network-synapix id per UI session — from bootstrap only.
2. **`current`** and **`latest`** are rejected on query endpoints.
3. Re-bootstrap after ops refresh; ids change when connectors recompile.
4. Never call vendor HTTP or Level-1 ports (`:9221`, `:19911`, `:19912`) from the client.

---

## 2. Map BFF read plane

Typical sequence after bootstrap:

| Step | Endpoint | Purpose |
|------|----------|---------|
| Graph | `POST …/graphs:query` | Paint topology + inventory overlay |
| Findings | `POST …/findings:query` | SOES/scanner findings (paginated) |
| Alerts | `POST …/alerts:query` | SAES alert firings |
| Vuln summary | `GET …/vulnerabilities/summary` | Estate chrome counts |
| Finding-context | `GET …/snapshot-sets/{id}/finding-context` | Placement / path header (no `includeRows=1` on map) |
| Reachability | `POST …/reachability:query` | Exact path when protocol/port + dest known |
| Briefings | `POST …/intelligence:briefing` | Cross-estate honesty header |

Finding-context header drives default board paint (`placementCounts`, `pathStatus`). Bulk row walks time out on large estates (~10k+ findings).

---

## 3. SEAP query plane

Coordinator: `http://127.0.0.1:7540/security-analysis/v1`

Every query needs **`analysisSnapshotId`** from bootstrap, or **`snapshotSetId` + `analysisVersion`**.

Core queries:

```http
POST …/exposures:query
POST …/paths:query
POST …/exploitability:query
POST …/exploitable-reachable:query
POST …/gaps:query
POST …/unknowns:query
POST …/unknowns:fetch
POST …/narratives:query
```

`paths:query` **scores** published path-risk assessments — it does not enumerate Yen chains. Empty with gap `no-canonical-ap-assessments` still allows possible paths from finding-context.

Map BFF proxies some SEAP calls (for example `unknowns:query`) when `:7540` is reachable.

---

## 4. Dossiers and facets

Inspector compose (not inventory chrome):

```http
POST /security-map/v1/tenants/default/dossiers:query
{
  "snapshotSetId": "<networkSynapixId>",
  "items": [{ "kind": "subject", "id": "…" }],
  "include": ["resource", "unknowns", "unknowns-questions", "narratives", "understanding-profile", "attackPaths", "possiblePath"]
}
```

`include` omitted → **`resource` only**. Unrequested facets are absent (fail closed).

| Facet | Use |
|-------|-----|
| `resource` | Level-1 subject/finding/alert body |
| `unknowns` | Per-entity gap rows |
| `unknowns-questions` | Governed question groups + object lists |
| `narratives` | Sidecar narrative snapshots |
| `understanding-profile` | Eleven governed security questions |
| `attackPaths` | Requires same `scenario` as `attack-paths:query` |
| `possiblePath` | PossiblePathSet summary from SEAP when published |

Classification facets (`semantic`, `framework`, `investigation`) need sidecar services up — omit unless requested.

---

# Paths and exposure — builders

# Paths and exposure — Client integrators

**Audience:** Engineers painting placement, possible paths, and attack-path overlay.  
**Related:** [Known unknowns](../03-known-unknowns/builders/BOOK.md)

---

## 1. Three claims do not collapse

| Claim | Source | Paint as | Never paint as |
|-------|--------|----------|----------------|
| CVE on Falcon subject | SOES `HAS_VULNERABILITY` | Host–finding inventory | Attack path, “reachable from internet” |
| Possible path | finding-context `placementClass`, `pathStatus`, `exposure` | **Candidate** network exposure | Attack path, “how they get in” |
| Attack path | `POST …/attack-paths:query` | Overlay when assessment returned | Default for every CVE |

A CVE on a host **does not** imply a path. A possible path **does not** imply exploitability. **`pathStatus: unknown`** = Path unknown — not “no path.”

---

## 2. Finding-context and possible paths

```http
GET /security-map/v1/tenants/default/snapshot-sets/{snapshotSetId}/finding-context
```

Header only for map chrome. Paint:

- `placementCounts` (exact / subnet / routed / policy / unplaced)
- Row `pathStatus`: `placed` | `unreachable` | `unknown` | `not-evaluated`
- `placementClass` / `pathTemplateClasses` / `exposure.kind`

If `policy.serviceStatus` is **`service-unknown`**, show candidate range — do not invent `tcp/443` or name a firing rule.

Inspector exact path:

```http
POST …/reachability:query
{
  "snapshotSetId": "<networkSynapixId>",
  "query": {
    "source": { "ip": "10.5.12.10" },
    "destination": { "ip": "8.8.8.8" },
    "service": { "protocol": "tcp", "port": 443 }
  }
}
```

Requires known protocol/port **and** explicit destination. Internet dest is never the default.

---

## 3. Attack-path overlay

Gate on bootstrap **`attackPathCapability.advertised`**, not merely route existence.

```http
POST /security-map/v1/tenants/default/attack-paths:query
{
  "snapshotSetId": "<networkSynapixId>",
  "scenario": {
    "kind": "inbound-exploit",
    "target": { "ip": "10.5.12.10" }
  }
}
```

`scenario.kind` required: `inbound-exploit` | `lateral-movement` | `egress-c2` | `observed-flow` | `custom-flow`.

Paint `maturityLevel`, `placementVerdict`, `networkVerdict`, `gaps[]`. **AP1** = placed in modeled scope — not “reachable from the internet.”

If `advertised` is false, still paint possible paths from finding-context.

---

## 4. SEAP scores and enumeration

SEAP sidecar (after network-synapix publish):

```http
POST http://127.0.0.1:7540/security-analysis/v1/tenants/default/exposures:query
POST …/paths:query
POST …/exploitable-reachable:query
```

**`exploitable-reachable:query`** = known exploitable **and** `networkVerdict === "reachable"` in one row. Not an attack-path overlay.

Structured possible-path enumeration (asset inspector only — not bulk):

```http
POST …/snapshot-sets/{id}/assets/{assetId}/possible-paths:query
{
  "originSelection": { "mode": "default-outside-in" },
  "serviceSelection": { "mode": "known-services" },
  "includeBlocked": true,
  "includeUnknown": true
}
```

Read `networkVerdict` and `observationVerdict` independently. Empty `paths[]` with populated `originCoverage[]` is **not** “no path.”

Never call Syncpix `:7520` from the UI — use Map BFF only.

---

# Known unknowns — builders

# Known unknowns — Client integrators

**Audience:** Engineers showing honest gaps, not silent “all clear.”  
**Related:** [Paths and exposure](../02-paths-and-exposure/builders/BOOK.md)

---

## 1. Honesty rules

**Known unknowns are first-class** on every network-synapix.

| Server signal | Paint as | Never paint as |
|---------------|----------|----------------|
| `pathStatus: unknown` / `not-evaluated` | **Path unknown** | “No path”, “isolated” |
| `placementStatus: unplaced` | **Not placed with current evidence** | “Not on FortiGate”, “off network” |
| `correlationState: unknown` | SCG unavailable — attribution open | Same as `unlinked` |
| `correlationState: unlinked` | Locator present, no join | Missing data bug |
| `crossFamilyJoin: not-attempted` | Alert and vuln estates **separate** | Hostname match = same asset |
| `scannerNotOnPin: true` | Vuln coverage = pinned families only | “No vulnerabilities” |
| `assurance-gap` narrative | Known-unknown **work item** | Confirmed risk |

---

## 2. Briefings and intelligence

Estate rollups:

```http
POST …/alerts:briefing
POST …/vulnerabilities:briefing
{ "snapshotSetId": "<networkSynapixId>", "format": "both" }
```

Read **`briefing.knownUnknown`** and **`briefing.gaps[]`** (count + samples per defect class).

Composed cross-estate header:

```http
POST …/intelligence:briefing
{ "snapshotSetId": "<networkSynapixId>", "format": "both" }
```

Use **`crossCutting.blockers[]`**, **`knownUnknown.crossFamilyJoin`** (always `not-attempted`). Does **not** join alert firings to open findings.

---

## 3. Work queue and questions

Unified actionable rows:

```http
POST …/unknowns:query
{
  "snapshotSetId": "<networkSynapixId>",
  "filters": { "fetchable": true, "limit": 100 }
}
```

Each row: `gapId`, `kind`, `why`, `fetch` (plane, connectorIds, operationIds).

Governed questions + object lists:

```http
POST …/unknowns:questions:query
{
  "snapshotSetId": "<networkSynapixId>",
  "format": "both"
}
```

**Close-order for UI:** (1) briefing `knownUnknown` on estate chrome, (2) **Known unknowns** panel from `unknowns:questions:query`, (3) `intelligence:briefing` blockers, (4) dossier `include=unknowns-questions`, (5) `assurance-gap` narratives separate from `security-situation`.

Also: `POST …/unplaced:query`, `POST …/path-unknown:query` for targeted slices.

---

## 4. Close-loop fetch

Ops-only close loop (Map proxies SEAP → live-tester):

```http
POST …/unknowns:fetch
{ "snapshotSetId": "<networkSynapixId>", "gapIds": ["…"], "dryRun": true }
```

Flow: `unknowns:query` → acquire via connector ops → `live:families:complete` refresh when needed → re-query.

Per-gap result status: `acquired` | `complete-empty` | `not-fetchable` | `credential-missing` | `review-only` | `error` | `planned` (dry-run).

**Empty vendor payload → `complete-empty`** (same gap remains) — never “no risk.”

Clients may expose fetch for operator workflows; do not auto-fetch from map paint loops.

---

# SEAP and narratives — builders

# SEAP and narratives — Client integrators

**Audience:** Engineers composing analysis sidecars on the same network-synapix.  
**Coordinator:** `http://127.0.0.1:7540/security-analysis/v1`

---

## 1. Analysis snapshot

SEAP publishes a **`SecurityExposureAnalysisSnapshot`** referenced by **`analysisSnapshotId`** on bootstrap.

Clients **must not** read Mongo partition collections or recompute scores.

`GET …/snapshots/{analysisSnapshotId}` returns **meta only** (counts, hashes). Estate rows come from query POSTs with `limit` + `cursor`.

Published objects include:

- `SecurityExposureAssessment` — exposure class, risk vector, gaps
- `ExploitabilityAssessment` — per-finding; CVSS/KEV/EPSS never prove local exploit alone
- `AttackPathRiskAssessment` — scores canonical AP assessments
- `PossiblePathSet` — compact refs on meta (~820 assets full-estate); full bodies via BFF query

Re-analyze after connector refresh: ops runs `live:analyze --set <snapshotSetId>`; clients re-bootstrap for new ids.

---

## 2. Exploitability and joins

```http
POST …/exploitability:query
POST …/exploitable-reachable:query
POST …/techniques:query
POST …/behaviors:query
```

**`exploitable-reachable:query`** requires both:

1. Known exploitability (KEV / exploit-available / ransomware-known), **and**
2. Path `networkVerdict === "reachable"`

CIDR `pathStatus: placed` alone does **not** qualify. Not an attack-path overlay.

Framework ATT&CK: empty until catalog snapshot on network-synapix + `behaviors:query`. Relevance ≠ observed.

Intel (NVD/KEV/EPSS) is a **snapshot on the network-synapix** acquired before analysis — SEAP never live-fetches public DBs in production.

---

## 3. Narratives sidecar

Deterministic narratives on the **same network-synapix** as SEAP. Never write Map-1.

```http
GET …/narrative-catalog
POST …/narratives:query
GET …/snapshot-sets/{snapshotSetId}/narratives
```

Use **`narrativeSnapshotId`** from bootstrap. Filters: `kind`, `family`, `type`.

Kinds include **`assurance-gap`** (known-unknown copy) vs **`security-situation`** — paint separately.

`live:analyze` (ops) compiles v2 + v2.1 + live-pattern v2.2 and advertises ids via Map `PUT …/analysis-refs`.

---

## 4. Understanding profiles

Question-support v2.1: **`SecurityUnderstandingProfile`** answers eleven governed security questions.

```http
GET …/understanding-profiles/{profileId}
GET …/subjects/{subjectType}/{subjectId}/understanding-profile?snapshotId=
```

Require explicit **`snapshotId`** (narrative snapshot) or resolve by **`understandingProfileId`** from bootstrap.

Dossier: `include=understanding-profile,narratives,unknowns,pathTechniques`.

Profiles are sidecar-only — identical snapshot+pins → canonical content. Missing overlay id → empty panel, not a fabricated story.

---

# Ops and lab pipeline — developers

# Ops and lab pipeline — Operators

**Audience:** Engineers publishing and refreshing network-synapix estates.  
**Not the client API** — clients use Map BFF `:9222` and SEAP `:7540` only.

---

## 1. Connectors and Syncpix

| Layer | Role | Examples |
|-------|------|----------|
| **Connectors** | Vendor acquisition + Map-1 abstraction | FortiGate, CrowdStrike, Splunk, Entra, Nessus |
| **Syncpix services** | Level-1 compile, graph, analysis | NRTS `:9221`, SOES `:19911`, SAES `:19912`, SCG `:19914` |
| **Product read** | Client contract | Map BFF `:9222`, SEAP `:7540`, Investigator `:7541` |
| **Skills** | Algorithms (ops/internal) | topology-path `:7520`, vuln-risk `:7522`, security-behavior `:7524` |
| **Lab host** | Pipeline orchestration | connector-live-tester `:9011` |

Pipeline law: connectors acquire → abstracts → topology scoper stages 0–4 → family compile → SCG snapshot-set → **network-synapix id** → auto or manual SEAP analyze.

---

## 2. Lab ports and hosts

| Port | Host | Surface |
|------|------|---------|
| `9222` | security-map-bff | `GET/POST /security-map/v1` |
| `7540` | seap-coordinator | `POST /security-analysis/v1` |
| `7541` | security-investigator | evidence-only IP reports |
| `9011` | live-tester | multistep, suites, unknowns fetch |
| `7520` | topology-path-skills | reachability, possible-paths (internal) |
| `9221` | nrts | Level-1 network (inspectors only) |
| `19911` | soes | Level-1 EDR (inspectors only) |
| `19914` | scg | snapshot-set publish |

Verify: Map, SEAP, topology-path healthy before client demos.

---

## 3. Refresh and compile

From repo root:

```bash
npm run live:families:complete          # Level-1 + SCG + auto SEAP
npm run live:families:complete -- --mode refresh   # coordinated refresh
npm run live:topology:complete          # NRTS stages 0–4 only
```

Live-tester `:9011` — multistep pipelines, continuous acquisition (FortiGate + Splunk + CrowdStrike), suite runs.

Intel merge (**before** SEAP): `cve-intel-merge` or vendor-acquired CVE fields on SOES findings.

Output: new or updated **`snapshotSetId`** logged as `live network-synapix scs:…`.

---

## 4. Analyze and verify

Analyze-only against existing network-synapix:

```bash
npm run live:analyze -- --set scs:17d3e1716f4812085529c89f
```

Or SEAP HTTP:

```http
POST http://127.0.0.1:7540/security-analysis/v1/tenants/default/runs
{ "request": { "mode": "analyze-only", "inputSet": { "snapshotSetId": "scs:…", … } } }
```

Verify publish:

1. `GET …/bootstrap` — `analysisSnapshotId`, `possiblePathsCapability`, finding-context version
2. SEAP meta — `counts.possiblePathSets` (full estate, no asset cap)
3. `POST …/exposures:query` with published id

**Data gaps that remain honest:** `service-unknown` from Falcon → 0 reachable AP verdicts until port enrichment; large Mongo docs may partition — analysis lives in coordinator + partition rows.

Narratives auto after analyze unless `NEO_NARRATIVES_AUTO=0`.
