Websites — worked graph designs

These designs describe intended system behavior and qualification requirements. Inspect the system atlas for named ports and relation participants.

Content website: browser, edge cache and source publication

Which revision did a reader receive through a cache?

Inspect this design · Definition JSON · revision fd73fca88045

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
↪ N12Route / navigation
▤ N11Surface / view component
↗ N19Tool / external API adapter
⇄ N25Cache / replica / sync agent
▰ N21Database / state authority
λ N34Build / compiler pass
▷ P11Execution / continuation
⇆ P02Request / response
▧ P23Artifact / schema reference
◎ P24Inspection / evidence
○ P01Value / immutable snapshot
↪ H36Navigation / interaction route
⇆ H05Request / reply binding
→ H02Value flow
▧ H26Artifact dependency
⊢ H27Derivation / provenance

Logic and ownership

An ordinary Kotlin renderer can compose a page model and apply URL routing. Cache keys include source and rendering revisions.

Failure and qualification

  • A cached page must name the served revision rather than imply it is the newest.
  • Public and authenticated render variants use separate visibility checks and cache policies.
  • Page render, edge publication and source ownership are separate projections of the same identified feature.

Hyperedge roles

RelationPatternParticipants, preserving role positions
routeH36 — Navigation / interaction route {#h36--navigation--interaction-route}provider[0]: route.page; consumer[0]: reader.route
fetchH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: edge.read; consumer[0]: reader.fetch
cacheH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: cache.lookup; consumer[0]: edge.cache
sourceH02 — Value flow {#h02--value-flow}provider[0]: cms.content; consumer[0]: build.source
publishH26 — Artifact dependency {#h26--artifact-dependency}provider[0]: build.page; consumer[0]: cache.artifact
provenanceH27 — Derivation / provenance {#h27--derivation--provenance}source[0]: cms.source; artifact[1]: build.artifact; served[2]: cache.served

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("web-content", "fd73fca88045ec746eb62d7e7fc93f25ca2aa0e690cc658cc2b300ec679e6cf5") {
    region("browser", "Browser")
    region("edge", "Edge services")
    region("data", "Data authorities")
    node("route", "URL routes", "N12", "browser", ports = listOf(
        PortDefinition("page", "P11", "PageRouteV1", PortPolarity.Offer)
    ))
    node("reader", "Page renderer", "N11", "browser", ports = listOf(
        PortDefinition("route", "P11", "PageRouteV1", PortPolarity.Require),
        PortDefinition("fetch", "P02", "PageReadV1", PortPolarity.Require)
    ))
    node("edge", "Edge page service", "N19", "edge", ports = listOf(
        PortDefinition("read", "P02", "PageReadV1", PortPolarity.Offer),
        PortDefinition("cache", "P02", "VersionedPageV1", PortPolarity.Require)
    ))
    node("cache", "Revision cache", "N25", "edge", ports = listOf(
        PortDefinition("lookup", "P02", "VersionedPageV1", PortPolarity.Offer),
        PortDefinition("artifact", "P23", "PageArtifactV1", PortPolarity.Require),
        PortDefinition("served", "P24", "PublicationRefV1", PortPolarity.Neutral)
    ))
    node("cms", "Content authority", "N21", "data", ports = listOf(
        PortDefinition("content", "P01", "ContentRevisionV1", PortPolarity.Offer),
        PortDefinition("source", "P24", "RevisionRefV1", PortPolarity.Neutral)
    ))
    node("build", "Page compiler", "N34", "data", ports = listOf(
        PortDefinition("source", "P01", "ContentRevisionV1", PortPolarity.Require),
        PortDefinition("page", "P23", "PageArtifactV1", PortPolarity.Offer),
        PortDefinition("artifact", "P24", "ArtifactRefV1", PortPolarity.Neutral)
    ))
    relation("route", "H36",
        Incidence("route", "page", "provider", 0),
        Incidence("reader", "route", "consumer", 0)
    )
    relation("fetch", "H05",
        Incidence("edge", "read", "provider", 0),
        Incidence("reader", "fetch", "consumer", 0)
    )
    relation("cache", "H05",
        Incidence("cache", "lookup", "provider", 0),
        Incidence("edge", "cache", "consumer", 0)
    )
    relation("source", "H02",
        Incidence("cms", "content", "provider", 0),
        Incidence("build", "source", "consumer", 0)
    )
    relation("publish", "H26",
        Incidence("build", "page", "provider", 0),
        Incidence("cache", "artifact", "consumer", 0)
    )
    relation("provenance", "H27",
        Incidence("cms", "source", "source", 0),
        Incidence("build", "artifact", "artifact", 1),
        Incidence("cache", "served", "served", 2)
    )
}

Commerce website: checkout, stock reservation and compensation

How do independent payment and inventory authorities coordinate?

Inspect this design · Definition JSON · revision b8f8390ef70a

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
▤ N11Surface / view component
⇢ N06Workflow / saga
⊂ N33Policy / principal boundary
▰ N21Database / state authority
⊟ N30External system boundary
≋ N24Queue / log / pubsub resource
↦ P04Push event
⊂ P20Authorization request / grant
⇆ P02Request / response
▣ P03Durable command / receipt
◎ P24Inspection / evidence
→ H02Value flow
⊂ H23Scoped authorization
⇆ H05Request / reply binding
↶ H17Compensation relation

Logic and ownership

A Kotlin state machine chooses reserve, charge, commit or compensate from durable saga state and observed receipts.

Failure and qualification

  • An H17 relation describes compensation obligations; it does not turn refunds into an exact inverse.
  • Recheck stock and price at the owning authority before committing.
  • Timeout is an unknown outcome until reconciled; repeated dispatch must be idempotent.

Hyperedge roles

RelationPatternParticipants, preserving role positions
intentH02 — Value flow {#h02--value-flow}provider[0]: view.order; consumer[0]: order.intent
grantH23 — Scoped authorization {#h23--scoped-authorization}provider[0]: auth.grant; consumer[0]: order.authority
stockH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: stock.reserve; consumer[0]: order.reserve
paymentH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: payment.charge; consumer[0]: order.payment
dispatchH02 — Value flow {#h02--value-flow}provider[0]: order.dispatch; consumer[0]: fulfil.command
sagaH17 — Compensation relation {#h17--compensation-relation}reservation[0]: stock.reservation; charge[1]: payment.chargeRef; coordinator[2]: order.compensation

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("web-commerce", "b8f8390ef70aa6f93223c465061ddbb86b45c2508669fbe8f280f44aeee945ca") {
    region("browser", "Browser")
    region("edge", "Edge services")
    region("data", "Data authorities")
    node("view", "Checkout Surface", "N11", "browser", ports = listOf(
        PortDefinition("order", "P04", "OrderIntentV1", PortPolarity.Offer)
    ))
    node("order", "Order saga", "N06", "edge", ports = listOf(
        PortDefinition("intent", "P04", "OrderIntentV1", PortPolarity.Require),
        PortDefinition("authority", "P20", "CheckoutGrantV1", PortPolarity.Require),
        PortDefinition("reserve", "P02", "ReservationV1", PortPolarity.Require),
        PortDefinition("payment", "P02", "ChargeV1", PortPolarity.Require),
        PortDefinition("dispatch", "P03", "DispatchV1", PortPolarity.Offer),
        PortDefinition("compensation", "P24", "SagaRefV1", PortPolarity.Neutral)
    ))
    node("auth", "Tenant / principal gate", "N33", "edge", ports = listOf(
        PortDefinition("grant", "P20", "CheckoutGrantV1", PortPolarity.Offer)
    ))
    node("stock", "Inventory authority", "N21", "data", ports = listOf(
        PortDefinition("reserve", "P02", "ReservationV1", PortPolarity.Offer),
        PortDefinition("reservation", "P24", "ReservationRefV1", PortPolarity.Neutral)
    ))
    node("payment", "Payment provider", "N30", "data", ports = listOf(
        PortDefinition("charge", "P02", "ChargeV1", PortPolarity.Offer),
        PortDefinition("chargeRef", "P24", "ChargeRefV1", PortPolarity.Neutral)
    ))
    node("fulfil", "Fulfilment queue", "N24", "data", ports = listOf(
        PortDefinition("command", "P03", "DispatchV1", PortPolarity.Require)
    ))
    relation("intent", "H02",
        Incidence("view", "order", "provider", 0),
        Incidence("order", "intent", "consumer", 0)
    )
    relation("grant", "H23",
        Incidence("auth", "grant", "provider", 0),
        Incidence("order", "authority", "consumer", 0)
    )
    relation("stock", "H05",
        Incidence("stock", "reserve", "provider", 0),
        Incidence("order", "reserve", "consumer", 0)
    )
    relation("payment", "H05",
        Incidence("payment", "charge", "provider", 0),
        Incidence("order", "payment", "consumer", 0)
    )
    relation("dispatch", "H02",
        Incidence("order", "dispatch", "provider", 0),
        Incidence("fulfil", "command", "consumer", 0)
    )
    relation("saga", "H17",
        Incidence("stock", "reservation", "reservation", 0),
        Incidence("payment", "chargeRef", "charge", 1),
        Incidence("order", "compensation", "coordinator", 2)
    )
}

Collaborative editor: sessions, replicas and workspace authority

What is shared state and what is ephemeral presence?

Inspect this design · Definition JSON · revision 5c6fa3ef17b1

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
▤ N11Surface / view component
⇄ N25Cache / replica / sync agent
▣ N05Actor / statechart
⊂ N33Policy / principal boundary
≋ N24Queue / log / pubsub resource
⌕ N22Query / materialized view
↦ P04Push event
⇄ P07Duplex session
◎ P24Inspection / evidence
⇆ P02Request / response
⊂ P20Authorization request / grant
→ H02Value flow
⌬ H06Multiparty session
⇆ H05Request / reply binding
⊂ H23Scoped authorization
⇄ H14Replica group

Logic and ownership

Kotlin applies a specified CRDT merge algorithm to deltas; graph queries expose participants and log position while ordinary code implements the algorithm.

Failure and qualification

  • Replica membership alone proves no convergence: cite the chosen CRDT operations and compaction rules.
  • Presence can expire without deleting durable document edits.
  • Read, write and invite grants differ; the server mediates every admitted update.

Hyperedge roles

RelationPatternParticipants, preserving role positions
editH02 — Value flow {#h02--value-flow}provider[0]: editor.delta; consumer[0]: replica.delta
sessionH06 — Multiparty session {#h06--multiparty-session}provider[0]: room.session; consumer[0]: replica.session
persistH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: log.append; consumer[0]: room.log
grantH23 — Scoped authorization {#h23--scoped-authorization}provider[0]: policy.grant; consumer[0]: room.authority
presenceH02 — Value flow {#h02--value-flow}provider[0]: room.presence; consumer[0]: presence.events
replicasH14 — Replica group {#h14--replica-group}local[0]: replica.state; durable[1]: log.position; coordinator[2]: room.epoch

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("web-collaboration", "5c6fa3ef17b10ec413e1e7580cdf88574aee3e4f70888ee887e8c3e6b1c1f0f9") {
    region("browser", "Browser")
    region("edge", "Edge services")
    region("data", "Data authorities")
    node("editor", "Document Surface", "N11", "browser", ports = listOf(
        PortDefinition("delta", "P04", "CRDTDeltaV1", PortPolarity.Offer)
    ))
    node("replica", "Local CRDT replica", "N25", "browser", ports = listOf(
        PortDefinition("delta", "P04", "CRDTDeltaV1", PortPolarity.Require),
        PortDefinition("session", "P07", "DocumentSessionV1", PortPolarity.Require),
        PortDefinition("state", "P24", "ReplicaRefV1", PortPolarity.Neutral)
    ))
    node("room", "Room actor", "N05", "edge", ports = listOf(
        PortDefinition("session", "P07", "DocumentSessionV1", PortPolarity.Offer),
        PortDefinition("log", "P02", "DocumentAppendV1", PortPolarity.Require),
        PortDefinition("authority", "P20", "DocumentGrantV1", PortPolarity.Require),
        PortDefinition("presence", "P04", "PresenceV1", PortPolarity.Offer),
        PortDefinition("epoch", "P24", "ActivationRefV1", PortPolarity.Neutral)
    ))
    node("policy", "Workspace policy", "N33", "edge", ports = listOf(
        PortDefinition("grant", "P20", "DocumentGrantV1", PortPolarity.Offer)
    ))
    node("log", "Document update log", "N24", "data", ports = listOf(
        PortDefinition("append", "P02", "DocumentAppendV1", PortPolarity.Offer),
        PortDefinition("position", "P24", "LogPositionV1", PortPolarity.Neutral)
    ))
    node("presence", "Presence TTL view", "N22", "edge", ports = listOf(
        PortDefinition("events", "P04", "PresenceV1", PortPolarity.Require)
    ))
    relation("edit", "H02",
        Incidence("editor", "delta", "provider", 0),
        Incidence("replica", "delta", "consumer", 0)
    )
    relation("session", "H06",
        Incidence("room", "session", "provider", 0),
        Incidence("replica", "session", "consumer", 0)
    )
    relation("persist", "H05",
        Incidence("log", "append", "provider", 0),
        Incidence("room", "log", "consumer", 0)
    )
    relation("grant", "H23",
        Incidence("policy", "grant", "provider", 0),
        Incidence("room", "authority", "consumer", 0)
    )
    relation("presence", "H02",
        Incidence("room", "presence", "provider", 0),
        Incidence("presence", "events", "consumer", 0)
    )
    relation("replicas", "H14",
        Incidence("replica", "state", "local", 0),
        Incidence("log", "position", "durable", 1),
        Incidence("room", "epoch", "coordinator", 2)
    )
}

Manna documentation: native reader, cited questions and PDF jobs

How is a question or PDF tied to one documentation revision?

Inspect this design · Definition JSON · revision d8473f6e841e

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
▤ N11Surface / view component
↗ N19Tool / external API adapter
▱ N26Blob / buffer / asset store
⌕ N22Query / materialized view
∑ N18Model inference
⇢ N06Workflow / saga
⇆ P02Request / response
○ P01Value / immutable snapshot
◎ P24Inspection / evidence
▣ P03Durable command / receipt
⇆ H05Request / reply binding
→ H02Value flow
≺ H03Control precedence
✓ H28Evidence supports claim

Logic and ownership

Lexical retrieval ranks source sections and passes a bounded context into the answer pipeline. A durable workflow renders the pinned edition separately from the request.

Failure and qualification

  • The implemented documentation feature has focused tests and a local preview; the whole Manna release is gated separately.
  • A source citation demonstrates where a statement came from, not that a design claim is implemented.
  • Export requires read and write authority, workspace ownership, pinned rendering revision and matching asset hashes.

Hyperedge roles

RelationPatternParticipants, preserving role positions
requestH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: api.read; consumer[0]: reader.request
sourceH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: corpus.fetch; consumer[0]: api.corpus
sectionsH02 — Value flow {#h02--value-flow}provider[0]: api.sections; consumer[0]: retrieval.documents
contextH02 — Value flow {#h02--value-flow}provider[0]: retrieval.context; consumer[0]: model.context
answerH02 — Value flow {#h02--value-flow}provider[0]: model.answer; consumer[0]: reader.answer
exportH03 — Control precedence {#h03--control-precedence}provider[0]: api.export; consumer[0]: job.start
storeH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: pdf.put; consumer[0]: job.output
evidenceH28 — Evidence supports claim {#h28--evidence-supports-claim}question[0]: reader.questionRef; sources[1]: retrieval.sourceRefs; answer[2]: model.answerRef

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("web-manna-docs", "d8473f6e841ee283895170c8af56c0af28ae8781077ec50ec1da959a23a840fe") {
    region("browser", "Browser")
    region("edge", "Edge services")
    region("data", "Data authorities")
    node("reader", "Native documentation reader", "N11", "browser", ports = listOf(
        PortDefinition("request", "P02", "DocumentationReadV1", PortPolarity.Require),
        PortDefinition("answer", "P01", "CitedAnswerV1", PortPolarity.Require),
        PortDefinition("questionRef", "P24", "QuestionRefV1", PortPolarity.Neutral)
    ))
    node("api", "Documentation service", "N19", "edge", ports = listOf(
        PortDefinition("read", "P02", "DocumentationReadV1", PortPolarity.Offer),
        PortDefinition("corpus", "P02", "CorpusReadV1", PortPolarity.Require),
        PortDefinition("sections", "P01", "SectionPackV1", PortPolarity.Offer),
        PortDefinition("export", "P03", "ExportIntentV1", PortPolarity.Offer)
    ))
    node("corpus", "Revisioned docs corpus", "N26", "data", ports = listOf(
        PortDefinition("fetch", "P02", "CorpusReadV1", PortPolarity.Offer)
    ))
    node("retrieval", "Bounded section retrieval", "N22", "edge", ports = listOf(
        PortDefinition("documents", "P01", "SectionPackV1", PortPolarity.Require),
        PortDefinition("context", "P01", "CitedContextV1", PortPolarity.Offer),
        PortDefinition("sourceRefs", "P24", "SourceRefSetV1", PortPolarity.Neutral)
    ))
    node("model", "Answer model adapter", "N18", "edge", ports = listOf(
        PortDefinition("context", "P01", "CitedContextV1", PortPolarity.Require),
        PortDefinition("answer", "P01", "CitedAnswerV1", PortPolarity.Offer),
        PortDefinition("answerRef", "P24", "AnswerRefV1", PortPolarity.Neutral)
    ))
    node("job", "PDF export workflow", "N06", "edge", ports = listOf(
        PortDefinition("start", "P03", "ExportIntentV1", PortPolarity.Require),
        PortDefinition("output", "P02", "EditionPutV1", PortPolarity.Require)
    ))
    node("pdf", "Reading edition store", "N26", "data", ports = listOf(
        PortDefinition("put", "P02", "EditionPutV1", PortPolarity.Offer)
    ))
    relation("request", "H05",
        Incidence("api", "read", "provider", 0),
        Incidence("reader", "request", "consumer", 0)
    )
    relation("source", "H05",
        Incidence("corpus", "fetch", "provider", 0),
        Incidence("api", "corpus", "consumer", 0)
    )
    relation("sections", "H02",
        Incidence("api", "sections", "provider", 0),
        Incidence("retrieval", "documents", "consumer", 0)
    )
    relation("context", "H02",
        Incidence("retrieval", "context", "provider", 0),
        Incidence("model", "context", "consumer", 0)
    )
    relation("answer", "H02",
        Incidence("model", "answer", "provider", 0),
        Incidence("reader", "answer", "consumer", 0)
    )
    relation("export", "H03",
        Incidence("api", "export", "provider", 0),
        Incidence("job", "start", "consumer", 0)
    )
    relation("store", "H05",
        Incidence("pdf", "put", "provider", 0),
        Incidence("job", "output", "consumer", 0)
    )
    relation("evidence", "H28",
        Incidence("reader", "questionRef", "question", 0),
        Incidence("retrieval", "sourceRefs", "sources", 1),
        Incidence("model", "answerRef", "answer", 2)
    )
}

Tenant dashboard: scoped routes and mediated data access

Where does tenant identity become an enforced constraint?

Inspect this design · Definition JSON · revision d818d03b6443

Diagram legend. Frames are named graph regions. Left/right pins show required/offered participation, not a universal schedule. Filled pins are connected; hollow pins are unbound. A junction card preserves one relation's role slots.

MarkerMeaning in this diagram
↪ N12Route / navigation
▤ N11Surface / view component
⊂ N33Policy / principal boundary
↗ N19Tool / external API adapter
▰ N21Database / state authority
▷ P11Execution / continuation
⇆ P02Request / response
⊂ P20Authorization request / grant
◎ P24Inspection / evidence
↪ H36Navigation / interaction route
⊂ H23Scoped authorization
⇆ H05Request / reply binding
⌕ H34Data access / lineage

Logic and ownership

Common Kotlin builds a typed query from validated request fields; server adapters materialize the verified tenant and principal context.

Failure and qualification

  • A tenant ID supplied by a browser does not authenticate tenancy.
  • Cross-workspace inspection is a separate capability and authority decision.
  • The same rules must apply to cached, background and agent-initiated reads.

Hyperedge roles

RelationPatternParticipants, preserving role positions
routeH36 — Navigation / interaction route {#h36--navigation--interaction-route}provider[0]: route.workspace; consumer[0]: surface.route
grantH23 — Scoped authorization {#h23--scoped-authorization}provider[0]: verify.context; consumer[0]: service.principal
readH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: service.dashboard; consumer[0]: surface.read
policyH23 — Scoped authorization {#h23--scoped-authorization}provider[0]: policy.grant; consumer[0]: service.policy
queryH05 — Request / reply binding {#h05--request--reply-binding}provider[0]: db.query; consumer[0]: service.storage
scopeH34 — Data access / lineage {#h34--data-access--lineage}principal[0]: verify.principalRef; policy[1]: policy.policyRef; authority[2]: db.authorityRef

Kotlin common declaration

Import dev.shibasis.reaktor.portgraph.definition.* for this structural declaration. Behavior stays in ordinary Kotlin and binds through qualified runtime ports and adapters. Declaring a graph does not activate a system.

graphDefinition("web-multitenant", "d818d03b64431dcd22809056e58e4e456ba90b6e7b2afdcbddce938cf6d7f0ef") {
    region("browser", "Browser")
    region("edge", "Edge services")
    region("data", "Data authorities")
    node("route", "Workspace route", "N12", "browser", ports = listOf(
        PortDefinition("workspace", "P11", "WorkspaceRouteV1", PortPolarity.Offer)
    ))
    node("surface", "Dashboard Surface", "N11", "browser", ports = listOf(
        PortDefinition("route", "P11", "WorkspaceRouteV1", PortPolarity.Require),
        PortDefinition("read", "P02", "DashboardReadV1", PortPolarity.Require)
    ))
    node("verify", "Principal verifier", "N33", "edge", ports = listOf(
        PortDefinition("context", "P20", "VerifiedContextV1", PortPolarity.Offer),
        PortDefinition("principalRef", "P24", "PrincipalRefV1", PortPolarity.Neutral)
    ))
    node("service", "Dashboard service", "N19", "edge", ports = listOf(
        PortDefinition("principal", "P20", "VerifiedContextV1", PortPolarity.Require),
        PortDefinition("dashboard", "P02", "DashboardReadV1", PortPolarity.Offer),
        PortDefinition("policy", "P20", "DataGrantV1", PortPolarity.Require),
        PortDefinition("storage", "P02", "ScopedQueryV1", PortPolarity.Require)
    ))
    node("policy", "Row / resource policy", "N33", "data", ports = listOf(
        PortDefinition("grant", "P20", "DataGrantV1", PortPolarity.Offer),
        PortDefinition("policyRef", "P24", "PolicyRefV1", PortPolarity.Neutral)
    ))
    node("db", "Workspace authority", "N21", "data", ports = listOf(
        PortDefinition("query", "P02", "ScopedQueryV1", PortPolarity.Offer),
        PortDefinition("authorityRef", "P24", "AuthorityRefV1", PortPolarity.Neutral)
    ))
    relation("route", "H36",
        Incidence("route", "workspace", "provider", 0),
        Incidence("surface", "route", "consumer", 0)
    )
    relation("grant", "H23",
        Incidence("verify", "context", "provider", 0),
        Incidence("service", "principal", "consumer", 0)
    )
    relation("read", "H05",
        Incidence("service", "dashboard", "provider", 0),
        Incidence("surface", "read", "consumer", 0)
    )
    relation("policy", "H23",
        Incidence("policy", "grant", "provider", 0),
        Incidence("service", "policy", "consumer", 0)
    )
    relation("query", "H05",
        Incidence("db", "query", "provider", 0),
        Incidence("service", "storage", "consumer", 0)
    )
    relation("scope", "H34",
        Incidence("verify", "principalRef", "principal", 0),
        Incidence("policy", "policyRef", "policy", 1),
        Incidence("db", "authorityRef", "authority", 2)
    )
}