Systems Architecture Spec Specification v0.1¶
This document is the normative specification of the Systems Architecture
Spec (SAS) data model, describing what is true of the sas package's
types and the JSON documents they marshal to and from.
SAS describes a software system as a typed graph — nodes, directed relationships, and boundaries — plus views (queries over that graph) and protocol bindings (instantiations of externally-defined choreography). Diagrams, threat models, launch-readiness checks, and change reports are all computed from this graph; none of them are the graph. The primary responsibility of this specification is the semantic model itself.
Every type in this specification conforms to the PlexusOne static-type
profile: no oneOf, anyOf, allOf, or other JSON Schema composition,
and no untyped or heterogeneous maps. Polymorphism is expressed with
explicit, named, optional fields (see §6, Extensions) rather than schema
composition, so the canonical Go model, its generated JSON Schema, and its
generated Zod/TypeScript types remain structurally isomorphic.
1. Architecture Document¶
An Architecture is the top-level SAS document.
version SAS document schema version, e.g. "0.1". Required.
metadata descriptive facts about the system (name, description, owner)
nodes every system, service, component, and actor. Required (may be empty).
relationships every directed edge between nodes
boundaries every network, trust, environment, account, region,
compliance, and organization boundary
views named queries over this architecture
bindings protocol bindings instantiating PIDL protocol definitions
2. Nodes¶
A Node is a system, service, component, or actor in the architecture graph.
id stable identity referenced by relationships, boundaries, and views. Required.
kind classification from the closed core vocabulary (§2.1). Required.
name human-readable name. Required.
owner who is responsible for this node, e.g. a team name
technology the concrete vendor product implementing this node (§2.2)
identity who or what this node runs as (§5)
boundaries IDs of every Boundary this node is a member of (§4)
criticality operational importance tier: tier0 | tier1 | tier2
assurance evidence references (§9)
refs pointers to detail owned by other specifications (§6.1)
extensions namespaced typed data for concerns outside the core shape (§6)
2.1 Kind taxonomy¶
kind is drawn from a small, closed, dotted vocabulary:
compute.function compute.instance compute.container
compute.sandbox data.database messaging.queue
gateway service agent
mcp_server external_service actor
compute.container is a containerized workload (an OCI container or Kubernetes
Pod); compute.sandbox is an isolated sandboxed execution environment (a
CaaS/code-interpreter sandbox or microVM). Both remain generic — a specific
runtime is expressed via technology, not a new kind.
This vocabulary MUST NOT grow to accommodate specific vendor products.
"AWS Lambda" is never a new kind value — it is kind: "compute.function"
plus technology: {provider: "aws", service: "lambda"} (§2.2). This keeps
generic analysis ("find all databases", "find externally exposed compute")
working identically across clouds and on-premises systems.
2.2 Technology¶
Technology names the concrete vendor product implementing a node,
independently of its core kind.
provider vendor or platform, e.g. "aws", "gcp", "k8s". Required.
service specific product within that provider, e.g. "lambda", "rds"
A provider/service pair that has no entry in a technology catalog
remains valid — catalogs are a display and analysis convenience, not a
closed vocabulary the core schema enforces.
3. Relationships¶
A Relationship is a directed edge between two nodes: from and to
node IDs, plus everything a security or threat-model review needs that a
plain arrow cannot carry.
id stable identity. Required.
from source node ID. Required.
to target node ID. Required.
kind classification from the closed core vocabulary (§3.1). Required.
transport protocol/port/encryption facts (§3.2)
operations specific verbs performed, from the generic vocabulary (§3.3)
identity which identity the caller acts as (§5)
authorization entitlements exercised (§5)
data data classifications carried across the wire
crossesBoundaries asserted boundary crossings (§4.1)
criticalPath whether this relationship sits on a critical execution path
sync "sync" | "async"
assurance evidence references (§9)
protocolRef a PIDL protocol this relationship participates in (§8)
extensions namespaced typed data (§6)
3.1 Kind taxonomy¶
calls data.access data_flow
publishes_to subscribes_to authenticates_to
authorizes_via observes enforces
depends_on spawns delegates_to
3.2 Transport¶
protocol transport-layer protocol, e.g. "tcp", "udp"
port network port
applicationProtocol application-layer protocol, e.g. "https", "postgresql", "grpc"
encryption "tls" | "mtls" | "none" | ...
3.3 Operations¶
operations is drawn from a generic, protocol-independent verb
vocabulary:
Protocol-specific operations (an HTTP method, a SQL statement, an MCP
action) specialize exactly one of these verbs via catalog-supplied
mappings (e.g. HTTP GET → read, SQL SELECT → read, MCP
tools/call → execute). A security-level view can therefore show
read while an implementation-level view shows HTTPS GET
/v1/orders for the same underlying fact.
4. Boundaries¶
A Boundary is an object, not a rectangle drawn around nodes. An AWS
account, a VPC, a trust zone, a FedRAMP authorization boundary, and a PCI
scope can all contain the same node simultaneously, so membership is
many-to-many by node ID reference (Node.boundaries), never a single
nesting hierarchy.
id stable identity referenced by Node.boundaries and
Relationship.crossesBoundaries. Required.
kind classification from the closed core vocabulary (below). Required.
name human-readable name. Required.
attributes simple key-value facts, e.g. {"vpc": "prod"}
compliance control-mapping detail (fedrampBoundary, controls[])
extensions namespaced typed data (§6)
Kind taxonomy:
sandbox is the isolation boundary around a sandboxed execution environment
(a CaaS/code-interpreter sandbox or microVM); container is the isolation
boundary around a container or Kubernetes Pod, distinct from the host and the
cluster.
4.1 Boundary crossing¶
A relationship crosses a boundary when exactly one of its from/to
endpoints is a member of it (per Node.boundaries). This is a derivable
fact, computed from node membership independently of whether an author
asserted it via Relationship.crossesBoundaries — the derived and
asserted sets MAY diverge, and a validator MAY report that divergence, but
the model itself does not require them to agree.
5. Identity & Entitlement¶
Identity describes who or what acts as a node, or which identity a relationship's caller assumes.
type human | workload | service | agent | device | external. Required.
mechanism aws_iam_role | spiffe | oauth_client | oidc | aauth |
k8s_service_account | x509 | api_key
ref the concrete identity, e.g. a SPIFFE ID or an IAM role ARN
An IdentityRef (used on Relationship.identity) either names a node
whose declared Identity applies (nodeId) or describes an identity
inline (identity).
An Entitlement is a subject-action-resource authorization tuple, shaped to map cleanly onto ReBAC systems (subject→user, action→relation, resource→object) without SAS itself evaluating or enforcing it:
subject identity or identity class, e.g. a node ID. Required.
action the entitled operation, e.g. "orders.insert". Required.
resource what the action applies to. Required.
conditions human/agent-readable qualifiers narrowing the grant
Authorization.entitlements carries the entitlements a relationship
exercises.
6. Extensions¶
Extensions carries namespaced, typed data for concerns that do not
belong in the core shape (node/relationship/boundary structure). Each
namespace is an explicit, statically typed struct field — security,
sre, compliance, agent in v0.1 — never a generic or dynamic map.
Adding a new extension namespace means adding a new named field and type
to the specification, not widening a dynamic bag: this is the mechanism
by which SAS remains extensible while staying within the static-type
profile.
6.1 External references¶
An ExternalRef points from a node or relationship to detail owned by another specification, so SAS never duplicates that content:
type openapi | pidl | threat-model-spec | multi-agent-spec |
terraform | otel. Required.
ref locator within that specification. Required.
7. Views¶
A View is a query over an Architecture, not a separate diagram file.
id stable identity. Required.
name human-readable name
includeKinds restrict to nodes of these NodeKind values
includeRelations restrict to relationships of these RelationKind values
includeBoundaries restrict to these boundary IDs
groupBy a BoundaryKind to nest selected nodes by (rendering hint)
level "generic" | "specific" (rendering hint)
Select(architecture, view) is a pure function producing the
sub-architecture a view denotes:
- Nodes: every node whose
kindis inincludeKinds, or every node whenincludeKindsis empty. - Relationships: every relationship whose
kindis inincludeRelations(or every relationship, whenincludeRelationsis empty) and whosefromandtoboth survived step 1. - Boundaries: every boundary whose ID is in
includeBoundaries, or — whenincludeBoundariesis empty — every boundary referenced by a selected node'sboundaries.
groupBy and level are rendering hints: they are carried on the View
but do not affect Select's output.
8. Protocol Bindings¶
A ProtocolBinding instantiates a PIDL protocol's abstract entities as concrete nodes in this architecture. SAS never duplicates PIDL's choreography.
id stable identity. Required.
protocolRef "pidl://<protocol-id>", where <protocol-id> matches the
referenced PIDL document's protocol.id field. Required.
participants map of PIDL entity ID -> SAS node ID. Required.
A binding is not required to assign a participant for every entity the referenced protocol declares. An unassigned entity is not an error — an architecture may legitimately not model every protocol participant (e.g. an ephemeral browser redirect) — but it is a fact tooling MAY surface.
9. Assurance¶
Assurance carries evidence references for a node or relationship: where to find proof that the declared element exists and behaves correctly. Assurance stores pointers to evidence, never the evidence itself, and this specification does not define reconciliation against live evidence.
tests test suite references, e.g. "test://integration/orders-db"
metrics observability references, e.g. "otel://service/orders-api"
detections security monitoring references, e.g. "siem://rule/..."
deployment deployment records, e.g. "aws://lambda/orders-api"
10. Profiles¶
A profile declares which optional semantics become mandatory for a given use case. Requesting a profile does not change what an Architecture document is structurally allowed to contain — the JSON Schema is identical regardless of profile — it changes which fields a validator requires to consider the document conformant to that profile.
| Profile | Requires |
|---|---|
development |
Nodes and relationships exist. No additional fields. |
deployment |
Every node declares technology and belongs to an account, region, or network boundary; every relationship declares transport. |
security |
Every boundary-crossing relationship (§4.1) declares transport.encryption and identity; every relationship with a write operation (create, update, delete, administer) declares authorization.entitlements; every internet-facing relationship (§10.1) additionally declares data classifications and its target node declares an owner. |
threat-model |
Every relationship touching an external_service node declares data classifications. |
sre |
Every tier0/tier1 node declares owner, an sre.availabilityTarget extension, and at least one assurance.metrics reference. |
10.1 Internet-facing relationships¶
A relationship is internet-facing when its source node's kind is
actor or external_service and it crosses a boundary (§4.1) — i.e.
traffic entering the architecture's own trust zone from a human actor or
an unmanaged external system, as distinct from the architecture calling
out to a third-party dependency. The security profile's internet-facing
requirements are the go-live gate: a conformant result under security
is intended to be the single check a launch process runs before a system
ships to production.
11. Threat Model Bridge¶
An Architecture MAY be exported as a system-under-analysis for
github.com/grokify/threat-model-spec's DiagramIR format: type: "dfd",
elements (from nodes), boundaries (from boundaries), and flows
(from relationships). This export carries structural facts only — threat
reasoning (attacks, STRIDE classification, mitigations, detections,
response actions) is not part of this specification and is not produced
by this export; element, boundary, and flow IDs are preserved unchanged
so a threat model can reference SAS IDs directly.
The mapping from NodeKind/BoundaryKind onto DiagramIR's narrower
ElementType/BoundaryType enums is lossy in specific, documented cases
(an actor node has no DFD equivalent and maps to external-entity; a
trust, compliance, or organization boundary has no DFD equivalent
and maps to network). Relationship.data.classifications (a free-text
list) is not mapped onto DiagramIR's single-value, four-tier
AssetClassification enum — there is no honest, general mapping between
the two — and is left for a human to set during threat-model authoring.
12. Non-goals (V1)¶
- Runtime reconciliation (comparing a declared Architecture against observed OpenTelemetry traces or cloud inventory). §9's Assurance model reserves the evidence-reference fields; no v1 tooling reconciles them.
- Semantic diff and change-impact classification, including FedRAMP significant-change assessment. Reserved for a future revision of this specification.
- CALM (FINOS) import/export. CALM's extensibility model depends on JSON
Schema composition (
allOf/anyOf), which this specification's static- type profile excludes by design; interop is demand-driven, not part of v1. - A
Flow/choreography concept duplicating PIDL.ProtocolBindingplusRelationship.protocolRefis the entire integration surface with PIDL; ordered protocol steps remain PIDL's responsibility. - Enforcement. This specification and its reference implementation validate and report; they do not block, proxy, or intercept traffic.
Prior art¶
The node/relationship/boundary model draws its "typed graph, not a
drawing" philosophy from architecture-as-code efforts generally (C4,
Structurizr, FINOS CALM) while deliberately diverging from CALM's
JSON-Schema-composition-based extensibility to keep the canonical model
representable as ordinary structs in Go, Rust, Swift, Java, and C# without
custom union-type machinery. The identity/entitlement model is shaped to
map cleanly onto ReBAC systems (OpenFGA, SpiceDB) for future authorization
backends without making this specification a ReBAC engine itself. Protocol
choreography is deliberately delegated to github.com/grokify/pidl rather
than reinvented; threat-model detail is deliberately delegated to
github.com/grokify/threat-model-spec.