Documentation
¶
Overview ¶
Package panorama owns Atlas's design-time ArchiMate models (ADR-0189). It deliberately has no dependency on the engine, processor, WAL, or runtime state: Panorama documents are architecture source, not execution facts.
Index ¶
- Constants
- Variables
- func AddElement(data []byte, add NewElement) ([]byte, string, error)
- func AddRelationship(data []byte, add NewRelationship) ([]byte, string, error)
- func AllowedBetween(sourceType, targetType string) []string
- func ApplyStatus(g *Graph, partial bool)
- func BindingKeys() []string
- func BucketSeconds(window ContextWindow) int64
- func ExportArchiMate(g Graph, opts ArchiMateExport) []byte
- func SetBinding(data []byte, elementID, key string, values []string) ([]byte, error)
- func SetLayout(data []byte, changes []LayoutChange) ([]byte, error)
- func WindowSeconds(window string) (int64, bool)
- func Windows() []string
- type AccessResolver
- type Application
- type ApplicationAccess
- type ArchiMateExport
- type Binding
- type BindingCandidate
- type BindingCandidates
- type BindingSet
- type Bucket
- type C4Element
- type C4Loss
- type C4Projection
- type C4Relationship
- type Call
- type Catalog
- type CatalogEdge
- type CatalogResolver
- type Clock
- type ContextDocument
- type ContextLimit
- type ContextQuery
- type ContextResolver
- type ContextResult
- type ContextWindow
- type Decision
- type DecisionUse
- type Draft
- type DriftDocument
- type DriftEntry
- type DriftLimit
- type Edge
- type ElementKind
- type EstateDomain
- type Fact
- type Facts
- type FactsResolver
- type Graph
- type IDGenerator
- type IncidentSite
- type Journal
- type Landscape
- type LandscapeCollector
- type LayoutChange
- type Measure
- type Mesh
- type Model
- type ModelElement
- type NewElement
- type NewRelationship
- type Node
- type Notation
- type NotationRelation
- type NotationType
- type Observation
- type ObservationDocument
- type ObservationSummary
- type Options
- type Overlay
- type OverlayCollector
- type Problem
- type Process
- type Product
- type ProductCatalog
- type ReachOut
- type Refusal
- type RelationshipKind
- type Resolution
- type ResolvedBinding
- type ResolvedValue
- type ResourceRef
- type Runtime
- type Service
- func (s *Service) CountForApplicationOnLoop(applicationID string) (int, error)
- func (s *Service) CountsByApplicationOnLoop() (map[string]int, error)
- func (s *Service) DeleteForApplicationOnLoop(applicationID string) error
- func (s *Service) HandleAddElement(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleAddRelationship(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleBindingCandidates(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleBindings(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleC4(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleContext(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleCreate(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleDrift(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleObservations(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleSetBinding(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleSetLayout(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleSubset(w http.ResponseWriter, _ *http.Request)
- func (s *Service) HandleUpdate(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleValidate(w http.ResponseWriter, r *http.Request)
- func (s *Service) HandleXML(w http.ResponseWriter, r *http.Request)
- func (s *Service) ModelsForApplicationOnLoop(applicationID string) ([]Summary, error)
- func (s *Service) OverlaysOnLoop(r *http.Request) ([]Overlay, error)
- func (s *Service) WithContextResolver(resolve ContextResolver) *Service
- type Status
- type Store
- type Subset
- type SubsetLimit
- type Summary
- type Target
- type UnavailableState
- type ValidationResult
- type Worker
- type WorkerUse
Constants ¶
const ( // StatusResolved: the resource exists and this caller may see it. StatusResolved = "resolved" // StatusForbidden: the resource exists, but outside this caller's access. Kept // distinct from missing on purpose — "you may not see it" and "nothing here has // that id" are fixed in different places, and one is a sharing decision while // the other is a typo or a deleted resource. Collapsing them would send // somebody to correct a model that is already correct. // // The disclosure this makes is that the id resolves to something. That id is // already in the caller's own model, which they can export; what the scope // withholds is the name, and the name is what stays out. StatusForbidden = "forbidden" // StatusMissing: nothing on this server has that id. StatusMissing = "missing" // StatusUnsupported: this server cannot resolve this kind of binding yet, // because nothing supplies a catalog for it. Distinct from missing because // missing is a claim — "nothing here has that id" — and it would be false: the // resolver did not look. A binding to a runtime id before the node descriptor // exists (ADR-0189 §6, delivered by P4) is the case this is for. StatusUnsupported = "unsupported" )
Binding resolution statuses.
const ( KeyApplicationID = "atlas.applicationId" KeyProcessID = "atlas.processId" KeyConnectorID = "atlas.connectorId" KeyJobType = "atlas.jobType" KeyRuntimeID = "atlas.runtimeId" KeyDeploymentTargetID = "atlas.deploymentTargetId" KeyReleaseID = "atlas.releaseId" // The business architecture (ADR-0308; // the register itself is ADR-0305). A capability record is what has to be // done, stated independently of how; an ArchiMate Capability is the architect's // drawing of the same thing. Bound, they are one architecture seen twice. Unbound, // the only thing connecting them is a name, and a name is exactly what gets // renamed. // // These bind the record's key rather than an opaque id, because the key *is* the // record's identity: it is the filename on disk, it is not renameable in place, and // it is what an export carries. Every other binding here holds an opaque id because // the resource's own name is mutable; a capability key is not, so the rule ADR-0189 // §4 states — carry the stable identifier, resolve the mutable rest at read time — // points at the key. KeyCapabilityKey = "atlas.capabilityKey" KeyValueStreamKey = "atlas.valueStreamKey" )
The binding keys. This is an allowlist, and that is load-bearing: ADR-0189 §4 forbids credential references, tokens, passwords and secret values in a binding, and an allowlist makes that structural. atlas.credentialRef is refused because it was never permitted, not because somebody remembered to ban it.
const ( C4Person = "Person" C4SoftwareSystem = "SoftwareSystem" C4Container = "Container" )
The C4 vocabulary this projection uses.
const ( // ContextSourceEvents is Atlas's own event log, exported to an OpenSearch index. // It is the only store that can speak about one process: every record carries // the definition key it belongs to. ContextSourceEvents = "events" // ContextSourceMetrics is a Prometheus-compatible store scraping this or another // Atlas node. It speaks about *nodes* and never about one process — see // ContextUnidentifiable. ContextSourceMetrics = "metrics" )
Context sources. Each names a store outside Atlas, because an answer's worth depends on where it came from and an operator disagreeing with one needs to know which system to go and argue with.
const ( // ContextNotConfigured: no such store is wired to this server. Nothing was // asked, so nothing is claimed. ContextNotConfigured = "not-configured" // ContextUnidentifiable: the store is wired and was not asked, because it cannot // name a thing of this kind at all. Atlas's metrics carry no per-element labels // by deliberate design (ADR-0142 forbids labelling by process id, instance key // or any other value the data can invent), so a metrics store can answer about a // node and never about one process. That is a property of the contract, not a // gap in the data, and reporting it as "nothing found" would send somebody to // look for a series that must not exist. ContextUnidentifiable = "unidentifiable" // ContextUnreachable: it was asked and could not be reached. Nothing is known. ContextUnreachable = "unreachable" // ContextRefused: it was reached and declined to answer — credentials, or a // permission on its side. Something is there and this server may not have it, // which is a different thing to tell an operator than "it is down". ContextRefused = "refused" // ContextEmpty: asked, answered, and holds nothing about this value in this // window. This is the only one of the six that is a statement about the // architecture rather than about the lookup. ContextEmpty = "empty" // ContextAvailable: asked, answered, and has something to show. ContextAvailable = "available" )
The states one source can be in for one bound value. They are six rather than two because every one of them sends somebody somewhere different, and collapsing them into "no data" would be the same lie this whole feature is arranged against: "nobody looked" and "somebody looked and found nothing" are not the same finding.
const ( Window1h = "1h" Window6h = "6h" Window24h = "24h" Window7d = "7d" )
The query windows. It is an allowlist rather than a free-form duration because a window is the bound on somebody else's cluster doing work for a page of ours: an arbitrary range is an arbitrary query, and the person who owns that cluster is not the person who typed it.
const ( KindApplication = "application" // KindProcess is one *deployment*: a definition held by one runtime since one // moment, and the thing that has instances, counters, incidents and a state // (ADR-0401 §2). The name stays `process` because that is what every reader, // route and export already calls it, and renaming a shipped node kind would cost // more than the precision is worth — what the split needed was a second kind for // the half that was missing, not a new word for this one. KindProcess = "process" // KindDefinition is what a model *is*, independent of any server holding it: // process id plus version (ADR-0401 §2). It has no state, no counters and no // runtime, because none of those are properties of a model — they belong to a // deployment of it. // // It is drawn only where a definition has more than one deployment. On a single // runtime a definition has exactly one, always — the collector reads the latest // deployment per process id — so a definition node there would stand in a fixed // 1:1 relation to a process node, which is a field rather than a second thing, // and would spend the measured 400-node budget (ADR-0211 §7) to restate what the // deployment already says. Where there are several it is the thing they have in // common, which is what makes "this process runs in three domains" a drawing. KindDefinition = "definition" // KindWorker is one configured Worker — a target and identity of a Worker Type // (ADR-0203). The store behind it is still the worker store and the model // still names it with connector="…"; those are the contracts that cannot move // yet, and this is new surface, so it says Worker. KindWorker = "worker" KindDecision = "decision" // KindDraft is a saved diagram nobody has deployed: it exists in the modeler and // the engine has never been given it. Distinct from KindProcess on purpose — the // rest of the mesh is a picture of what this server *runs*, and a draft is the // one node on it that runs nothing. Distinct from KindUnresolved too: nothing is // missing here, the work simply has not been deployed yet. // // Drafts are off by default and collected only when a caller asks for them // (ADR-0211 §7): an estate typically holds several drafts per deployed process, // so carrying them always would spend most of the size budget on diagrams that // are not part of the running landscape at all. KindDraft = "draft" // KindRestricted is a resource that exists but which this caller may not see. // It stands in for a real node so the edge to it survives (ADR-0211 §3). KindRestricted = "restricted" // KindUnresolved is a dependency nothing on this server provides: a call target // with no deployment, or a worker name nobody configured. Distinct from // restricted on purpose — "not here" and "not yours to see" are different // findings, and an operator chasing a broken dependency needs to tell them apart. KindUnresolved = "unresolved" // KindTarget is a deployment target: a peer Atlas this server can promote to // (ADR-0189 §6). It is the only kind on this landscape whose state comes from // outside this process, and therefore the only one that can be *unreachable* or // *stale* — every other node is read from local state while the request is being // served, so it can neither fail to be contacted nor go out of date. KindTarget = "target" // KindCatalog is one service catalogue (ADR-0312): what a group of people may // order, and the arrangement of it. It is a design-time record like every other // node here, and — unlike a process or a peer — nothing can be asked how it is, // so it carries no observation at all. KindCatalog = "catalog" // KindProduct is one catalogue item: a service that provisions itself, or a // product assembled from several that do. One kind and not two, because Atlas // holds one record for both and the difference is in the edges: a thing with // parts is a whole, and the same record offered alone is a service. Deriving two // kinds from that shape would invent a distinction the store does not make, and // the store is right — the same laptop is a product in one catalogue and a part // of a workplace bundle in another. KindProduct = "product" )
Node kinds. The mesh keeps these namespaced by prefix in Node.ID so a renderer can route a click without parsing the kind separately.
const ( EdgeContains = "contains" EdgeCalls = "calls" // EdgeUses is a process depending on something that is not a process: a // configured worker, or a decision it delegates to. A product uses the processes // that provision and revoke it, which is the same relationship seen from the // catalogue's side: the thing that needs work done, pointing at what does it. EdgeUses = "uses" // EdgeOffers is a catalogue putting a product in front of the people it reaches. // Deliberately not EdgeContains: an application *holds* its processes and they // exist nowhere else, while a product is referenced by catalogues rather than // owned by one (ADR-0315) — two catalogues offering one laptop are two offers of // one thing, not two laptops. EdgeOffers = "offers" // EdgeComposition and EdgeAggregation are the arrangement of a product: the part // that always comes with the whole, and the part offered beside it. // // They are kept apart here for the reason the catalogue keeps them apart: they // are different promises to whoever orders. They are also the first two edges on // this landscape that are *exactly* what ArchiMate means by those words, which is // why the projection can name them instead of declaring a loss. EdgeComposition = "composition" EdgeAggregation = "aggregation" // EdgeRequires is precedence between two products: the one this points at has to // be provisioned first. It is what the fulfilment order is computed from, and the // only catalogue edge whose direction means "after". EdgeRequires = "requires" // EdgeDeploys runs from a definition to a deployment of it (ADR-0401 §2). It is // the one edge on this picture whose two ends are different *kinds* of thing // rather than two resources: a model, and a runtime holding it. // // Deliberately not EdgeContains, which says an application holds processes that // exist nowhere else. A definition is the opposite case — the whole point of // drawing it is that the same model exists in more than one place. EdgeDeploys = "deploys" )
Edge kinds.
const ( // ProvenanceDerived: Atlas has this resource and no model binds to it. ProvenanceDerived = "derived" // ProvenanceModeled: a model declares this and Atlas does not have it. This is // the half a drawn-only view can never show — the architecture says something // exists and the instance disagrees. ProvenanceModeled = "modeled" // ProvenanceBoth: Atlas has it and a model binds to it. ProvenanceBoth = "both" )
Provenance says how a node is known (ADR-0211 §2). The three are the point of overlaying a model onto the mesh: without them you have two pictures and no relationship between them.
const ( // SubjectLandscape is the default: the estate as it runs. SubjectLandscape = "" // SubjectProducts is the catalogue and what provisions it. SubjectProducts = "products" )
The subjects a starmap can be drawn about.
They are two pictures and not one picture with more on it, which is what the first cut of the catalogue got wrong. The landscape answers "what does this server run and what depends on what"; an operator reading it for a stuck process does not want a hundred products in the way, and — the part that is not a matter of taste — every product spends the size budget (§7), so a large catalogue could collapse somebody else's landscape to applications without them ever having asked to see it.
The product map answers the other question: what is offered, what each product is assembled from, and which processes have to run for it to be delivered. It carries the processes the products *bind* and nothing else of the estate — not the workers those processes use, not the applications that hold them, not the peers. One hop, because the second hop is the landscape's question and the landscape is one click away.
const ( StateHealthy = "healthy" StateDegraded = "degraded" StateNotReady = "not-ready" StateUnreachable = "unreachable" StateStale = "stale" // StateUnbound is ADR-0189 §6's "unbound/unknown": there is no observation to // have, or none applies. It is the default, and it is not a finding. StateUnbound = "unbound" )
Observation states (ADR-0189 §6). StateUnmodeled is deliberately absent: Atlas finding a resource no model mentions is already reported, per node, as provenance (ADR-0211 §2), and repeating it as a status would make every node of an unmodeled instance carry a finding.
const ( SeverityOK = "ok" SeverityAttention = "attention" SeverityCritical = "critical" // SeverityUnknown is the neutral rendering, not a fourth level of badness. Most // nodes in a young landscape are unbound, and coloring them as a problem makes // the whole mesh a problem. SeverityUnknown = "unknown" )
Severity classes (ADR-0211 §4).
const ( // NotationAtlas is the landscape drawn as itself: Atlas's own kinds, no // projection, nothing to declare. NotationAtlas = "atlas" // NotationC4 is the C4 projection. It is not a peer notation and never an // authoring one — ADR-0211 §8's "projection id that is not a peer notation". NotationC4 = "c4-projection" )
const ( SourceDeployments = "deployments" SourceInstances = "instances" SourceWorkers = "workers" SourceReleases = "releases" SourceNode = "node" // SourceRemote is a peer Atlas that was asked. It is named apart from the local // sources because it is the only one that can be out of date or out of reach, // and a reader deciding how much to trust a row needs to know which it is. SourceRemote = "remote" // SourceNone marks a binding no source on this server can say anything about. // It is a source in the payload precisely so that the absence is attributable: // "nobody looked" and "somebody looked and found nothing" are different // findings, and the second one is a claim. SourceNone = "none" )
Observation sources. A state is only as good as what produced it, so every observation names its source rather than presenting one anonymous verdict — an operator disagreeing with a finding needs to know where to go and look.
const ( LayerStrategy = "strategy" LayerBusiness = "business" LayerApplication = "application" LayerTechnology = "technology" )
The ArchiMate layers this subset spans, in the order the palette shows them: motivation-free, top-down, the way the standard's own layer diagram reads.
const ( AspectActive = "active" // who or what performs behaviour AspectBehavior = "behavior" // what happens AspectPassive = "passive" // what is acted upon )
The three aspects this subset needs. ArchiMate's fourth, motivation, is not in the authoring subset at all: a Goal or a Requirement relates to everything else through a different part of the matrix, and offering the elements without those rules would be offering a palette that cannot be connected to anything.
const ( // RefusedOutOfSubset: Atlas does not author this element or relationship type // yet. The document may legitimately contain it, and reading one is unaffected. RefusedOutOfSubset = "out-of-subset" // RefusedByNotation: ArchiMate does not permit this relationship between these // two elements. No version of Atlas will allow it. RefusedByNotation = "not-in-archimate" // RefusedSelfReference: an element related to itself. The standard permits it // for a few relationships; Atlas does not author it, because on a canvas it is // almost always a mis-drop rather than a statement. RefusedSelfReference = "self-reference" )
The reasons a connection is refused. They are told apart because they send somebody to different places: one is a gap in this build, the others are facts about the notation and about the document.
const ( // NotationArchiMate32 is the stable notation id carried by Panorama resources. // ArchiMate 3.2 uses the 3.1 Open Exchange schema, whose XML namespace stayed // at /3.0/; the notation and the transport schema are related but not identical // version numbers. NotationArchiMate32 = "archimate-3.2" // ExchangeNamespace is the target namespace of The Open Group ArchiMate 3.1 // Model Exchange File Format. It is also the namespace used by Atlas's existing // generated ArchiMate 3.2 model under docs/architecture/model. ExchangeNamespace = "http://www.opengroup.org/xsd/archimate/3.0/" // MaxXMLBytes bounds a persisted Panorama document and every validation pass. // Architecture models are design-time input; they must never become an // unbounded parser workload merely because the API accepted a large body. MaxXMLBytes = 4 << 20 // 4 MiB )
const BindingContractVersion = 1
BindingContractVersion is the version of this key set. It is a small public contract: the keys below are what another tool can rely on finding, so a change to their meaning is a version bump rather than a quiet redefinition.
Adding a key is not such a change, and the business-architecture keys were added without one (ADR-0308). Version 1 says what a client may rely on finding, and every promise it made still holds: no key's meaning moved, none was removed, and a document carrying the new keys reads correctly under the old contract — the extractor already ignores what it does not recognise, and a property outside atlas.* was never read at all. Bumping on an addition would make the number mean "something here changed", and a version that changes when nothing a client depends on has changed is one clients stop reading.
const C4MappingVersion = 1
C4MappingVersion versions the mapping below. It is a small public contract: a change in what an ArchiMate type projects to is a version bump, not a quiet redefinition, because exported pictures outlive the code that made them.
const ContextContractVersion = 1
ContextContractVersion is the shape of the document below.
const DefaultWindow = Window24h
DefaultWindow is what a caller who names none gets. A day is the span in which "has it been like this" is usually asked, and it is short enough that the buckets below stay readable.
const DriftContractVersion = 1
DriftContractVersion is the shape of the document below.
const EdgePromotes = "promotes"
EdgePromotes is the one estate-wide edge ADR-0402 §4 admits: an application join a promotion recorded. It is a fact rather than a drawing — `deploymentTarget.Bindings` holds it, written when a promotion succeeded — and it says a promotion *happened*, never that the application is still deployed there, which nothing on this side can know.
const KindDomain = "domain"
KindDomain is one Atlas installation in an estate. It is the only node kind at this altitude: an estate of domains is what the picture is, and anything smaller belongs to the landscape a domain expands into.
const NotationC4Projection = "c4-projection"
NotationC4Projection is the projection's own id. It is deliberately not a peer of archimate-3.2 in the notation list: nothing is authored in it.
const NotationMappingVersion = 2
NotationMappingVersion identifies the tables below. §8 requires a projection's mapping to be explicit *and* versioned: a reader who saw a picture last quarter has to be able to tell whether it would be drawn the same way today. Bump it whenever a row changes meaning.
Serving the relationship table did not bump it, and that is a judgment rather than an oversight. No row changed meaning: the three relationship types were already what the exporter wrote at version 1, and a document generated then is what a document generated now would be, relationship for relationship. What changed is who can read the table — the canvas as well as the exporter — and a version that moved for that would say a document might differ when it cannot.
Drawing the service catalogue did bump it, to 2. The landscape gained two element types and two relationship types, and — the part that actually forces the bump — the sentence "three relationships is all there can ever be" stopped being true: a catalogue holds real Composition and real Aggregation, which is the first thing on this landscape that ArchiMate can name exactly. A document generated at version 1 and one generated now are different documents about one estate, and that is the question this number exists to answer.
const ObservationContractVersion = 1
ObservationContractVersion is the shape of the document below. It is versioned for the same reason the binding contract is: what a consumer parses outlives the code that produced it, and a field that changes meaning without a version bump is a silent lie to whatever already reads it.
const StateUnserved = "unserved"
StateUnserved is ADR-0402 §3's fifth case: **the peer answered, and does not serve this view.** An older build whose descriptor advertises no `panorama.mesh` feature is neither unreachable nor stale, and folding it into either sends an operator to the wrong place — unreachable sends them to look at a network, stale implies there was once an answer. It is a version boundary, and the descriptor's derived feature list is what makes it distinguishable at all.
It is not a fault: severityOf leaves it at SeverityUnknown, the neutral rendering, because a peer that is simply older is an ordinary state of affairs.
const SubsetVersion = 2
SubsetVersion is the version of the table below. It is a public contract — a palette and a rule the browser enforces — so a change to what is permitted is a version bump rather than a quiet redefinition.
Version 2 adds ValueStream (ADR-0308). This one *is* a bump where the binding contract's addition was not, and the difference is what each number promises. The binding contract says which keys a client may rely on finding, and adding one takes nothing away from that. This table says what Atlas will create and which connections the canvas will refuse mid-drag — so a browser holding version 1 and a server on version 2 disagree about a live rule, and the browser would refuse an arrow the server permits. The version is how the two notice.
Variables ¶
var ErrShuttingDown = errors.New("panorama: server is shutting down")
ErrShuttingDown is what a FactsResolver returns when it could not take its run-loop turn because the server is closing. It is a sentinel rather than a message because the handler has to answer 503 rather than 500 for it: a document built from facts nobody gathered would report every element as unobserved, and "the server is going away" is a different thing to tell a caller than "something broke".
Functions ¶
func AddElement ¶
func AddElement(data []byte, add NewElement) ([]byte, string, error)
AddElement writes a new element into the model and places it on a view.
Both halves happen or neither does. An element with no shape is invisible in the editor that just created it, and a shape referencing an element that is not there is what the parser reports as a broken view — either alone is a worse document than the one we started with.
func AddRelationship ¶
func AddRelationship(data []byte, add NewRelationship) ([]byte, string, error)
AddRelationship writes a new relationship into the model and draws it on a view.
The subset decides whether it may exist at all, through the same function the canvas asked before it let the arrow be drawn — so a connection the canvas offered is one this accepts, and a connection it refused never gets here.
func AllowedBetween ¶
AllowedBetween lists the relationship types this subset will author between two element types, in menu order. It is what a connect menu offers, so that the menu only ever contains choices that will be accepted.
func ApplyStatus ¶ added in v0.21.0
ApplyStatus is [applyStatus] for a graph built outside this package: the release lens (ADR-0495 §4) draws a release's recorded architecture with Panorama's renderer, and its severities and status summary have to come from the one mapping above rather than from a second copy of it.
func BindingKeys ¶
func BindingKeys() []string
BindingKeys lists the contract's keys in a stable order, for an API that has to tell a client what it may set.
func BucketSeconds ¶
func BucketSeconds(window ContextWindow) int64
BucketSeconds is how wide one bucket of a window is. It divides the window into at most maxContextBuckets intervals, so a longer window gets coarser buckets rather than a longer series — the panel's width does not change with the span.
func ExportArchiMate ¶
func ExportArchiMate(g Graph, opts ArchiMateExport) []byte
ExportArchiMate writes the graph as an ArchiMate Model Exchange document.
The graph is whatever the caller was already allowed to see: this generates from a derived graph rather than re-reading anything, so the scope filtering, the restricted placeholders and the size budget all apply exactly as they did to the picture. There is nothing here that could disclose more than the landscape did.
func SetBinding ¶
SetBinding sets one binding key on one element to the given values, replacing whatever that key held before. No values clears it. Properties that are not this key — Atlas's other keys and anybody else's properties alike — are untouched.
func SetLayout ¶
func SetLayout(data []byte, changes []LayoutChange) ([]byte, error)
SetLayout writes new geometry for the named view nodes and leaves every other byte of the document alone.
A change naming a node the document does not have is refused rather than ignored. The canvas can only move what it drew, so an unknown id means the two have disagreed about what the document contains — and quietly dropping it would save a layout that is not the one on screen.
func WindowSeconds ¶
WindowSeconds reports how long a window is, and whether it is one this contract allows.
Types ¶
type AccessResolver ¶
type AccessResolver func(r *http.Request, applicationID string) (ApplicationAccess, error)
AccessResolver resolves application ownership on the API run loop. A resolver may read the server's application store because Service invokes it only from a loop turn; it must not call Loop.Do recursively.
type Application ¶
Application is one Atlas process application as the mesh sees it. CanView is resolved by the server against the caller's sharing scope (ADR-0071) before the graph is derived — the mesh never decides access itself, it only honors it.
type ApplicationAccess ¶
ApplicationAccess is the caller's resolved access to the Atlas process application that owns a Panorama model. The API server supplies this result from its existing project/application scopes; Panorama does not maintain a second ACL model.
type ArchiMateExport ¶
type ArchiMateExport struct {
// Instance names the Atlas the landscape was read from — a host, as the reader
// knows it. Never a credential and never a peer's address: this is the server the
// reader is already looking at.
Instance string
// GeneratedAt is when, in Unix seconds. Zero says so rather than inventing one.
GeneratedAt int64
}
ArchiMateExport is what a generated document says about where it came from.
type Binding ¶
type Binding struct {
ElementID string `json:"elementId"`
ElementType string `json:"elementType"`
// ElementName is what the architect called it. The mesh overlay shows it, and
// for an element Atlas has no resource for it is the only name there is.
ElementName string `json:"elementName,omitempty"`
Key string `json:"key"`
Values []string `json:"values"`
}
Binding is every id bound under one key on one element. Values is a list because the exchange format expresses many-to-many by repeating the property, and ADR-0189 §4 requires that: one ArchiMate component can be implemented by several Atlas process applications, and one process application can contribute to several components.
type BindingCandidate ¶
BindingCandidate is one resource a caller may bind to: an id to store and a name to show. Nothing else — a candidate list is a picker, not a resource export.
type BindingCandidates ¶
type BindingCandidates struct {
Key string `json:"key"`
// Supported is false when this server cannot resolve the kind at all. Without
// it an empty list would read as "there are none", which is a different and
// wrong answer.
Supported bool `json:"supported"`
Candidates []BindingCandidate `json:"candidates"`
}
BindingCandidates answers "what may I bind this key to".
type BindingSet ¶
type BindingSet struct {
ContractVersion int `json:"contractVersion"`
Bindings []Binding `json:"bindings"`
Problems []Problem `json:"problems"`
}
BindingSet is what one document declares, plus every reason a declaration was refused. Problems are carried rather than returned as an error: a model with one bad binding still has good ones, and hiding them all behind a single failure would make a typo look like an empty landscape.
func ExtractBindings ¶
func ExtractBindings(data []byte) (BindingSet, error)
ExtractBindings reads every Atlas binding an Open Exchange document declares.
It never rewrites the input. Reading a model must not be able to change it, and the byte-for-byte guarantee is what keeps unsupported-but-standard content (ADR-0189 §2) safe across a round trip.
The pass is two-phase because the schema puts propertyDefinitions near the end of the model, after the elements that reference them: a property cannot be resolved on sight, so references are collected and resolved once the document is read.
type Bucket ¶
Bucket is one interval of a measure: the moment the interval starts, in Unix seconds, and the value observed across it.
type C4Element ¶
type C4Element struct {
ID string `json:"id"`
Type string `json:"type"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
// Parent is the enclosing element, from a composition or aggregation.
Parent string `json:"parent,omitempty"`
SourceType string `json:"sourceType"`
}
C4Element is one projected element. Source is the ArchiMate identifier it came from, so a reader can always get back to the authored model.
type C4Loss ¶
type C4Loss struct {
ID string `json:"id"`
SourceType string `json:"sourceType"`
Name string `json:"name,omitempty"`
Reason string `json:"reason"`
}
C4Loss is one thing the projection could not express, and why.
type C4Projection ¶
type C4Projection struct {
Notation string `json:"notation"`
SourceNotation string `json:"sourceNotation"`
SourceModelID string `json:"sourceModelId"`
SourceRevision int64 `json:"sourceRevision"`
MappingVersion int `json:"mappingVersion"`
// ReadOnly is always true and is in the payload on purpose: a consumer must not
// be able to mistake a projection for a source document.
ReadOnly bool `json:"readOnly"`
Elements []C4Element `json:"elements"`
Relationships []C4Relationship `json:"relationships"`
Dropped []C4Loss `json:"dropped"`
}
C4Projection is a read-only view of an ArchiMate model in C4 terms.
func ProjectToC4 ¶
func ProjectToC4(data []byte, modelID string, revision int64) (C4Projection, error)
ProjectToC4 projects an Open Exchange document into C4 terms.
type C4Relationship ¶
type C4Relationship struct {
ID string `json:"id"`
Source string `json:"source"`
Target string `json:"target"`
Name string `json:"name,omitempty"`
SourceType string `json:"sourceType"`
}
C4Relationship is one projected arrow. SourceType names the ArchiMate relationship it came from, because C4 has only one kind and the distinction is otherwise lost.
type Call ¶
type Call struct {
ElementID string
// ElementIndex is the same element by the identity the per-element counters are
// keyed on (ADR-0080). It is what lets this edge carry a traversal count.
ElementIndex int32
CalledProcessID string
TargetKey uint64
}
Call is one call activity, already resolved by the server to the deployment it actually reaches. TargetKey is zero when nothing on this server provides the called process. Resolution belongs to the server because a call's effective target depends on deploy-time facts and call overrides (ADR-0076), not on anything the compiled model carries.
type Catalog ¶
type Catalog struct {
Applications map[string]ResourceRef
Processes map[string]ResourceRef
Connectors map[string]ResourceRef
JobTypes map[string]ResourceRef
Runtimes map[string]ResourceRef
Targets map[string]ResourceRef
Releases map[string]ResourceRef
// Capabilities and ValueStreams are the business-architecture register (ADR-0305),
// bound as ADR-0308 decides. Unlike the maps above they are not filtered per caller, and that is
// a property of the register rather than an omission here: a capability record is
// readable by any signed-in identity, because it says what the organisation must
// be able to do and nothing about what this server runs. What *is* scoped are the
// processes a capability names as its realisations, and those are resolved
// elsewhere, through their own sharing scope.
Capabilities map[string]ResourceRef
ValueStreams map[string]ResourceRef
}
Catalog is what the server supplies to resolve a document's bindings: one lookup per binding kind, already filtered for the caller. Each key resolves against its own map — looking a process id up among applications would report every binding as missing, which reads as a broken model rather than as a broken resolver.
type CatalogEdge ¶ added in v0.7.0
CatalogEdge is one arrangement edge as stored, before the mesh decides whether it is drawable. Kind is the catalogue's own spelling (composition, aggregation, requires, excludes) rather than a mesh edge kind, because deciding which of them this picture draws is exactly this package's job and not the store's.
type CatalogResolver ¶
CatalogResolver supplies the Atlas resources a document's bindings resolve against, already filtered for this caller (ADR-0189 §4). Like AccessResolver it runs inside a run-loop turn, so it may read the server's stores directly and must not call Loop.Do recursively.
type ContextDocument ¶
type ContextDocument struct {
ContractVersion int `json:"contractVersion"`
ElementID string `json:"elementId"`
ObservedAt int64 `json:"observedAt"`
Window ContextWindow `json:"window"`
Results []ContextResult `json:"results"`
// Truncated reports that results were dropped to stay inside the bound, so a
// short answer is not read as a complete one.
Truncated bool `json:"truncated"`
Limits []ContextLimit `json:"limits"`
}
ContextDocument is what a caller asking "has it been like this" gets, for one element.
It is scoped to one element rather than to a model on purpose. Every bound value costs a query against somebody else's cluster, and a model-wide answer would multiply that by the whole landscape for a panel that shows one element at a time.
func AssembleContext ¶
func AssembleContext(elementID string, window ContextWindow, results []ContextResult, now int64) ContextDocument
AssembleContext builds the document from what the sources returned.
It bounds and orders; it never invents. A result missing from the input is missing from the output, because a placeholder this function made up would be indistinguishable from an answer a source actually gave.
type ContextLimit ¶
ContextLimit is one thing this surface cannot do, with the reason.
type ContextQuery ¶
type ContextQuery struct {
Key string
Value string
Window ContextWindow
}
ContextQuery is one lookup the server is asked to perform: a bound value, and the window to ask about.
func QueriesFor ¶
func QueriesFor(set BindingSet, elementID string, window ContextWindow) []ContextQuery
QueriesFor lists the lookups one element's bindings imply, in a stable order.
A binding whose value no source can identify still produces a query: the answer *unidentifiable* is one this document owes the reader, and dropping the binding here would make it indistinguishable from an element that binds nothing.
type ContextResolver ¶
type ContextResolver func(r *http.Request, queries []ContextQuery) ([]ContextResult, error)
ContextResolver asks whichever historical stores are wired about one element's bound values, and returns one result per (source, value) pair it considered.
Like FactsResolver it is called *off* the run loop and takes its own turns for the parts that need one: every adapter here talks to a store outside this process, and holding the single writer across that is the one thing every other request is waiting for it not to do (invariant I3).
It returns a result for every pair it considered, including the ones it could not answer. A resolver that returned only its successes would make "no store is wired" and "the store holds nothing" the same answer, which is exactly the conflation the six states exist to prevent.
type ContextResult ¶
type ContextResult struct {
Source string `json:"source"`
Key string `json:"key"`
Value string `json:"value"`
State string `json:"state"`
// Reason is why the state is what it is, in the source's own words. It is
// required for every state except available: a source that could not answer owes
// the reader a sentence, and one that could does not need to explain itself.
Reason string `json:"reason,omitempty"`
Measures []Measure `json:"measures,omitempty"`
Detail map[string]string `json:"detail,omitempty"`
}
ContextResult is one source's answer about one bound value.
type ContextWindow ¶
type ContextWindow struct {
Window string `json:"window"`
From int64 `json:"from"`
To int64 `json:"to"`
}
ContextWindow is the span a result covers, in Unix seconds.
func NewContextWindow ¶
func NewContextWindow(window string, now int64) (ContextWindow, bool)
NewContextWindow resolves a requested window name against the allowlist and anchors it at now.
type Decision ¶
Decision is one local DMN decision a business-rule task delegates to. A remote decision, evaluated by a worker rather than in this engine, is deliberately not one of these: it arrives as a WorkerUse instead, which is where its dependency actually points.
type DecisionUse ¶ added in v0.7.0
type DecisionUse struct {
DecisionID string
// ElementIndex is the calling business rule task. See [Call.ElementIndex].
ElementIndex int32
}
DecisionUse is one business rule task and the local decision it calls.
A slice of these rather than of decision ids, which is what this carried before: one decision called from two tasks is one edge reached from two places, and the edge's count is their sum (ADR-0400). Collapsing them to the decision first loses half of it, and the compiler's own de-duplicating enumeration is still the right one for the deploy-time gate that reads it.
type Draft ¶
Draft is one saved-but-not-deployed diagram. It carries far less than a Process because there is far less to know: a draft has no deployment key, no version, no instances and no observation, and inventing any of them would put a runtime claim on a thing that has never run.
A draft whose process id is already deployed is not one of these. The server leaves it out, because that draft and the deployed process are the same work seen at two altitudes — drawing both would put a twin beside every process on the canvas and say nothing true about either.
type DriftDocument ¶
type DriftDocument struct {
ContractVersion int `json:"contractVersion"`
Entries []DriftEntry `json:"entries"`
// Since is the moment from which this document can speak, or zero when this
// model has never been read. It is the honest bound on every question asked of
// it: nothing before it can be answered, whether or not anything happened.
//
// While nothing has been dropped that is when the journal *started watching*,
// not the oldest entry it holds — a quiet hour is a real answer, and dating the
// document from its first entry would throw that hour away. Once entries have
// been dropped it is the oldest retained entry instead, because everything
// before that one genuinely is gone.
Since int64 `json:"since"`
// Truncated reports that entries were dropped to stay inside the bound, so an
// empty stretch is not read as a quiet one.
Truncated bool `json:"truncated"`
Limits []DriftLimit `json:"limits"`
}
DriftDocument is what a caller asking "what changed" gets.
type DriftEntry ¶
type DriftEntry struct {
ElementID string `json:"elementId"`
Key string `json:"key"`
Value string `json:"value"`
// From is the state this value was last seen in, and To the state it is in
// now. Both travel because the direction is the finding: healthy → degraded and
// degraded → healthy are the same transition seen from opposite sides, and one
// of them is somebody's incident and the other is somebody's fix.
From string `json:"from"`
To string `json:"to"`
// Reason is the new state's own sentence, kept so an entry read a week later
// still says what happened rather than only that something did.
Reason string `json:"reason,omitempty"`
At int64 `json:"at"`
}
DriftEntry is one recorded change of one bound value's state.
type DriftLimit ¶
DriftLimit is one thing this journal cannot see, with the reason.
type Edge ¶
type Edge struct {
From string `json:"from"`
To string `json:"to"`
Kind string `json:"kind"`
// Taken is how many times the elements behind this edge were activated, summed:
// two business rule tasks calling one decision are one edge reached from two
// places, and the count is theirs together (ADR-0400). It is read from the
// cumulative per-element counter ADR-0080 already maintains, so it costs a join
// rather than an index.
//
// **Nil and zero are different facts.** Nil means the counter was not read — the
// same absence [Process.Runtime] uses — or that this edge kind has no element to
// anchor on. Zero means it was read and nothing has taken this path, which is a
// real answer and the one that must never be rendered as a verdict: a quarterly
// reconciliation, a compensation branch and an error handler are each correctly
// zero for months and each load-bearing.
Taken *int64 `json:"taken,omitempty"`
// Promoted is how many applications an estate join stands for (ADR-0402 §4): one
// edge between two domains, and the number of recorded promotions along it. Zero
// on every other kind of edge, where it would be a claim about nothing.
Promoted int `json:"promoted,omitempty"`
// TakenSince is when the counting started, in Unix seconds: the moment the source
// definition was deployed, because the counter is keyed by definition key and a
// redeploy starts a fresh one. It is the window without which Taken says nothing,
// so it travels with it and is zero exactly when Taken is nil.
TakenSince int64 `json:"takenSince,omitempty"`
}
Edge is one directed relationship between two nodes.
From, To and Kind are its identity: three call activities to one target are one dependency, and the derivation deduplicates on exactly those three. Taken and TakenSince are facts *about* that identity and are never part of it — a count in the key would turn one dependency back into several.
type ElementKind ¶
type ElementKind struct {
// Type is the xsi:type written into the document — the standard's own name,
// because the document is an interchange format and not Atlas's private store.
Type string `json:"type"`
// Label is what a person reads in the palette; Layer groups it there.
Label string `json:"label"`
Layer string `json:"layer"`
// Aspect is the element's role in the standard's structure/behaviour split, and
// it is what most of the relationship rules below are actually about. Carrying
// it makes those rules readable as the sentences the specification states,
// rather than as a list of type pairs somebody has to trust.
Aspect string `json:"aspect"`
}
ElementKind is one authorable element type.
func AuthorableElements ¶
func AuthorableElements() []ElementKind
AuthorableElements returns the element subset, in palette order.
type EstateDomain ¶ added in v0.7.0
type EstateDomain struct {
// ID addresses the domain: the deployment target's id, or this runtime's own handle.
//
// Deliberately not the runtime id, although ADR-0401 §1 makes
// `(runtimeId, key)` an entity's estate-wide name. A peer that never answered has no
// runtime id to be named by, and §3 says such a peer is still a shape on the picture —
// so the node is addressed by what an operator configured, which always exists, and
// the runtime id travels beside it where the peer actually reported one.
ID string
Name string
// RuntimeID is what the domain said it was, and empty where it said nothing.
RuntimeID string
// Holds is how many nodes that domain's own landscape holds, where it answered. It is
// the number that makes §2's budget legible to a reader: a domain standing for 400
// nodes and one standing for four are the same size on this picture, and only this says
// they are not.
Holds int
// Restricted is how many of [EstateDomain.Holds] were placeholders standing in for
// what the credential that drew this domain may not see. It is the other half of
// [EstateDomain.DrawnBy]: naming the credential says who looked, and this says how
// much of the domain that look did not reach.
Restricted int
// State and Reason are the observation the collector resolved (ADR-0189 §6), including
// [StateUnserved].
State string
Reason string
// Applications maps a local application id to the id the same application has on this
// domain, as a promotion recorded it. Only peers have it, and its size is what the join
// stands for.
Applications map[string]string
// DrawnBy names the credential whose reach produced this domain's answer, because §1
// requires the picture to state whose credential drew each subgraph: a federated read is
// as wide as the credential that made it, and an incompleteness that is stated is a
// fact while one that is not is a discovery.
//
// Empty for this runtime, which was drawn for the reader by the reader's own rights.
DrawnBy string
}
EstateDomain is one domain as the collector found it — this runtime, or a peer.
Everything here is either operator configuration or something the domain reported. Nothing is inferred: a field the peer did not report stays empty, so a reader can tell the difference between "nothing there" and "nobody asked".
type Fact ¶
type Fact struct {
Source string
State string
Reason string
// Detail is a small, flat set of specifics — a version, a count — for a reader
// who wants the number behind the sentence. It is bounded on the way out
// (see boundDetail): this document is fetched per model view, and an unbounded
// detail object is how one becomes too big to render.
Detail map[string]string
}
Fact is one runtime fact the server observed about one Atlas resource. The server builds these; this package only projects them onto the elements that bind to them.
type Facts ¶
type Facts struct {
Applications map[string]Fact
Processes map[string]Fact
Connectors map[string]Fact
JobTypes map[string]Fact
Runtimes map[string]Fact
Targets map[string]Fact
Releases map[string]Fact
}
Facts is what the server supplies, one lookup per binding kind, already filtered for the caller. As with Catalog, a nil map and an empty map are different answers: nil means nothing on this server observes that kind at all, empty means it was looked at and holds none.
type FactsResolver ¶
FactsResolver supplies the runtime facts an observation document is projected from, already filtered for this caller.
Unlike CatalogResolver it is called *off* the run loop, and takes its own loop turns for the parts that need one. That inversion is deliberate: gathering these facts includes asking peer servers (ADR-0189 §6), and holding the single writer for the duration of a network call is the one thing every other request on this server is waiting for it not to do (invariant I3). A resolver whose on-loop half could not run returns ErrShuttingDown.
type Graph ¶
type Graph struct {
Nodes []Node `json:"nodes"`
Edges []Edge `json:"edges"`
// RuntimeID is the runtime that derived this subgraph, and it is the half that
// makes every key in the document mean something (ADR-0401 §1). A node id here
// carries a key, a key carries its partition but not its installation, and two
// installations both mint partition 0 counter 1 — so "a bare key is never
// published estate-wide", and this is what stops these from being bare.
//
// Qualifying the document rather than each id is deliberate: the estate-wide name
// is the pair `(runtimeId, key)`, and both halves already existed, so nothing new
// is minted and no shipped id scheme moves. A reader stitching several subgraphs
// is the one that has to qualify per node, and it has what it needs to.
//
// Empty where the server could not read its own identity, which is a fact about
// the reading and not an estate of one: an absent runtime id means these keys
// cannot be placed, and a consumer must refuse to join them rather than assume
// they are local.
RuntimeID string `json:"runtimeId,omitempty"`
// Restricted is how many placeholder nodes stand in for resources this caller
// may not see. The legend states it, so a filtered picture says that it is
// filtered instead of looking complete.
Restricted int `json:"restricted"`
// Clustered reports that the graph exceeded its size budget and collapsed to
// applications.
Clustered bool `json:"clustered"`
// The desired-versus-observed comparison, when a model was overlaid.
// Modeled counts what a model declares and Atlas does not have; Unmodeled counts
// what Atlas has and no model mentions.
Modeled int `json:"modeled"`
Unmodeled int `json:"unmodeled"`
// OutOfScope counts bindings to kinds this picture does not draw — releases,
// deployment targets, runtimes. They are neither matched nor absent, and the
// count keeps that visible instead of silently dropping them.
OutOfScope int `json:"outOfScope"`
// Status is the severity summary and, as importantly, the declaration of which
// observation states this build cannot produce at all (ADR-0211 §4).
Status Status `json:"status"`
// ObservedAt is when this picture was derived, in Unix seconds, and zero when
// the caller supplied no clock.
//
// It is on the payload rather than left to the reader's own clock because the
// picture outlives the tab it was drawn in: ADR-0211 §10 requires an exported
// landscape to carry its observation time *into* the artifact, and a time the
// browser invented would date the export rather than the facts in it. The two
// differ by however long the file sat unsaved, and by all of history once
// somebody re-exports a stale tab.
ObservedAt int64 `json:"observedAt,omitempty"`
}
Graph is one derived mesh.
func DeriveEstate ¶ added in v0.7.0
func DeriveEstate(local EstateDomain, peers []EstateDomain) Graph
DeriveEstate assembles the estate picture from this runtime and its peers.
The local domain is first, because a reader starts where they are and an estate that draws its peers and omits itself is a picture of somebody else's estate. Peers follow in id order: map iteration must not reach the payload, or two reads differ for no reason a reader can act on.
func DeriveGraph ¶
DeriveGraph computes the mesh for one caller's landscape.
type IDGenerator ¶
IDGenerator mints an opaque Panorama model id off the run loop.
type IncidentSite ¶
type IncidentSite struct {
// ElementID is the BPMN element id, which is what an operator searches the
// diagram for.
ElementID string `json:"elementId"`
// ElementType is the BPMN type ("ServiceTask", "CallActivity", …), so the id
// reads as a thing rather than as a string.
ElementType string `json:"elementType,omitempty"`
// Count is how many incidents are parked on this element.
Count int `json:"count"`
// Message is the first one seen here, not a summary of all of them: several
// incidents on one element usually share a cause, and inventing a combined
// sentence would be writing a message nobody produced. Operations has the rest.
Message string `json:"message,omitempty"`
}
IncidentSite is one element of a process with unresolved incidents on it.
"Three tokens are parked" tells somebody there is a problem; "three tokens are parked on the service task charge-card, and the last one said 502 Bad Gateway" tells them where to go. That is the difference between a status view somebody glances at and one they act on, and it costs the same read either way — the engine already knows which element each incident is stuck on, because that is how it resolves one.
The element is named by its BPMN id and type rather than by a label. Only user tasks carry a human title in a compiled process, so a label would be present for some elements and absent for others — and an identifier that is sometimes there is worse than one that is always there. It is also what the Operations view shows, so the two name the same thing the same way.
type Journal ¶
type Journal struct {
// contains filtered or unexported fields
}
Journal records what changed between one observation document and the next.
It carries its own lock rather than living on the run loop: observations are computed off the loop (a document can wait on peer servers), so this is written from request goroutines and putting it behind the single writer would mean holding that writer across a network call.
func NewJournal ¶
func NewJournal() *Journal
func (*Journal) Document ¶
func (j *Journal) Document(modelID string) DriftDocument
Document returns one model's journal, newest first — which is the order somebody asking "what changed" reads in.
func (*Journal) Record ¶
func (j *Journal) Record(modelID string, doc ObservationDocument) ObservationDocument
Record folds one observation document into the journal and returns the document enriched with what it now knows about each value's last change.
The first read of a model records nothing. That is deliberate: everything would otherwise arrive as a transition from nothing, and a journal whose first page is always a full inventory teaches a reader to skip the first page.
type Landscape ¶
type Landscape struct {
// RuntimeID is the runtime these facts were read off — this server, for a local
// collection (ADR-0401 §1). Every process here inherits it unless it carries its
// own, which is what a subgraph read from a peer would.
RuntimeID string
Applications []Application
Processes []Process
// Drafts are the saved diagrams nobody has deployed, and are empty unless the
// caller asked for them — see [Draft].
Drafts []Draft
Workers []Worker
Decisions []Decision
// Catalogs and Products are the service catalogue (ADR-0312), drawn always
// rather than on request like the drafts: a catalogue is part of what this
// server runs, not a plan somebody saved, and an installation that does not use
// the portal has none and pays nothing for them.
Catalogs []ProductCatalog
Products []Product
// Targets are the peers this server can promote to, and what asking them
// produced. Filled in two halves: the collector names them on the run loop, and
// [ReachOut] supplies each one's state off it.
Targets []Target
// PartialStatus reports that the server stopped counting parked work before it
// had seen all of it. It travels with the landscape because only the collector
// knows it, and it must reach the payload: without it a process the scan never
// reached would be published as healthy on no evidence at all.
PartialStatus bool
// ObservedAt is when these facts were read off the server, in Unix seconds, and
// zero from a collector that does not date its work.
//
// It is on the landscape rather than only in [Options] because a collector may
// answer from something it read earlier: the facts are what is dated, and a
// picture stamped with the moment it was *served* would claim a freshness nobody
// measured — the undated "all green" of §10, with a timestamp on it, which is
// worse than none. A collector that reads afresh every time sets it to now and
// nothing changes.
ObservedAt int64
}
Landscape is everything the mesh derives from, already filtered for this caller.
type LandscapeCollector ¶
LandscapeCollector reads the server's resources and returns them filtered for this caller: every Application and Process it yields carries the CanView the caller's sharing scope resolves to (ADR-0071). It runs on the API run loop, so it may read the project and deployment registries directly and must not call Loop.Do recursively.
The mesh takes it as a function rather than reaching for the stores itself. That is ADR-0147's rule — a per-area service holds a loop and takes every other dependency explicitly — and it is also what keeps this package free of the server object it would otherwise have to import. It also hands back the part of the landscape that cannot be read from local state — see ReachOut — or nil when there is none.
type LayoutChange ¶
type LayoutChange struct {
// NodeID is the view node's `identifier`, not the element it references. A view
// may place the same element more than once, and each placement moves on its own.
NodeID string `json:"nodeId"`
X int `json:"x"`
Y int `json:"y"`
W int `json:"w"`
H int `json:"h"`
}
LayoutChange is one view node's new geometry, in the document's own units.
type Measure ¶
type Measure struct {
// Name is the stable identifier a client may key on; Label is what a person
// reads. Both travel because one of them is a contract and the other is prose,
// and using either for both jobs makes it impossible to change the prose.
Name string `json:"name"`
Label string `json:"label"`
// Total is the measure across the whole window. It is carried rather than left
// to be summed from the buckets, because for a gauge the sum of the buckets is
// not the total and a client cannot tell which kind it has.
Total float64 `json:"total"`
Buckets []Bucket `json:"buckets"`
}
Measure is one named series over the window.
type Mesh ¶
type Mesh struct {
// contains filtered or unexported fields
}
func NewMesh ¶
func NewMesh(loop *runloop.Loop, collect LandscapeCollector, overlays OverlayCollector, maxNodes int, now Clock) *Mesh
NewMesh builds the mesh service. maxNodes is the size budget; zero is unlimited. overlays may be nil, in which case no desired-versus-observed comparison is made. now stamps each answer with the moment its landscape was read; a nil clock leaves the stamp off rather than inventing one.
func (*Mesh) Derive ¶ added in v0.7.0
Derive is the landscape this caller would be served, handed to something that draws a different picture out of it — today the estate altitude's local domain, which is this server's own landscape counted rather than drawn (ADR-0402 §2).
It writes the failure response itself and reports false, exactly as HandleGraph does, because the two must not come to disagree about what a collection that could not run looks like: a shutting-down loop is a 503 and never an empty picture.
Deriving the whole landscape to take a number off it is deliberate. The estate says how many nodes a domain holds, and a reader who expands that domain has to find the same number: counting anything cheaper here would be a second derivation, and two derivations of one picture eventually disagree.
func (*Mesh) HandleArchiMate ¶
func (m *Mesh) HandleArchiMate(w http.ResponseWriter, r *http.Request)
HandleArchiMate serves the landscape as an ArchiMate Open Exchange document.
It derives exactly what HandleGraph derives and writes that, so the file and the picture are the same landscape seen twice rather than two answers to one question. Whatever the caller may not see is missing from both, identically, and the document says how much of it there was.
func (*Mesh) HandleGraph ¶
func (m *Mesh) HandleGraph(w http.ResponseWriter, r *http.Request)
HandleGraph serves the whole-instance mesh for the calling principal.
func (*Mesh) HandleNotations ¶
func (m *Mesh) HandleNotations(w http.ResponseWriter, _ *http.Request)
HandleNotations serves the vocabularies the landscape can be drawn in, with each one's mapping and what it drops (ADR-0211 §8).
It is served rather than duplicated in the browser for the reason ADR-0189's connection subset already gives: three things read this table — the picture's labels, the stamp on its image export, and the ArchiMate document generated from it — and a copy in the view would eventually have the picture calling a node one thing while the file called it another.
No authorization beyond the route's own: this is a description of a mapping, not of anybody's resources.
type Model ¶
type Model struct {
ID string `json:"id"`
ApplicationID string `json:"applicationId"`
Name string `json:"name"`
Notation string `json:"notation"`
Revision int64 `json:"revision"`
XML string `json:"xml"`
CreatedAt int64 `json:"createdAt"`
CreatedBy string `json:"createdBy,omitempty"`
UpdatedAt int64 `json:"updatedAt"`
UpdatedBy string `json:"updatedBy,omitempty"`
}
Model is one application-owned Panorama architecture document. XML remains the canonical source; the surrounding fields provide Atlas ownership, optimistic concurrency, and cheap listings without parsing every document.
type ModelElement ¶
type ModelElement struct {
ElementID string
ElementType string
Name string
Key string
Values []string
}
ModelElement is one bound ArchiMate element: what the architect called it, and which Atlas ids its binding names (ADR-0189 §4).
type NewElement ¶
type NewElement struct {
// Type is an ArchiMate element type from the authoring subset. Anything else is
// refused: this writer will not create what the canvas cannot draw rules for.
Type string `json:"type"`
Name string `json:"name"`
// ViewID is the view the new shape is placed on, and the geometry is where.
ViewID string `json:"viewId"`
X int `json:"x"`
Y int `json:"y"`
W int `json:"w"`
H int `json:"h"`
}
NewElement is one element to add, and where it goes on a view.
type NewRelationship ¶
type NewRelationship struct {
Type string `json:"type"`
Source string `json:"source"`
Target string `json:"target"`
ViewID string `json:"viewId"`
}
NewRelationship is one relationship to add, and the view to draw it on.
type Node ¶
type Node struct {
ID string `json:"id"`
Kind string `json:"kind"`
Name string `json:"name,omitempty"`
Provenance string `json:"provenance"`
// Application is the owning application's node id, for grouping. Empty on
// application, restricted, and unresolved nodes.
Application string `json:"application,omitempty"`
// Catalog is the catalogue node a product is grouped under, and empty on every
// other kind. It is its home catalogue (ADR-0315) where that catalogue is on this
// picture, and otherwise the first catalogue here that offers it — a product
// whose home this reader cannot see is still somewhere, and a collapsed picture
// has to put it there.
//
// Separate from Application rather than sharing it: that field carries a process
// application, and one field holding two kinds of container is how a reader ends
// up asking which one they have.
Catalog string `json:"catalog,omitempty"`
// ProductState and ApprovalKind are a product node's two catalogue facets, and
// are empty on every other kind — see [Product.State] and [Product.Approval]
// for why each carries the store's own spelling rather than a flag.
//
// ProductState is deliberately *not* the State field further down. That one is
// the observation state behind Severity — whether this thing is healthy — and
// these two are what the catalogue says the offering is. A withdrawn product
// whose provisioning process is green is an ordinary state of affairs, and one
// field answering both questions is how a reader ends up reading the wrong one.
//
// They reach only products this caller may see: a product they may not is drawn
// as a restricted placeholder of another kind entirely and never gets here, so
// no facet of a hidden offering leaves the server.
ProductState string `json:"productState,omitempty"`
ApprovalKind string `json:"approvalKind,omitempty"`
// ProcessID and Version identify a process node well enough to navigate to the
// Operations view (L2) without a second lookup. On a definition node they are the
// identity itself rather than a navigation aid (ADR-0401 §2).
ProcessID string `json:"processId,omitempty"`
Version int32 `json:"version,omitempty"`
// RuntimeID is the runtime holding this deployment — the other half of its
// estate-wide name (ADR-0401 §1). Empty on a definition node, which belongs to no
// runtime, and on the kinds that are estate-wide by nature: a capability, a value
// stream, a product and a published interface are named without one, and needing
// to qualify one of those by the server it sits on would mean it was modelled at
// the wrong altitude.
//
// Not to be confused with Runtime further down, which is instance counters. This
// field says *where* the deployment is; that one says what has run in it.
RuntimeID string `json:"runtimeId,omitempty"`
// Definition is the definition node a deployment realises, and is set only where
// that definition is drawn — which is where it has more than one deployment. Empty
// otherwise, including on the single-runtime case that is every installation
// today: the definition is still known there, it is simply the deployment's own
// (ProcessID, Version) and needs no second node to hold it.
Definition string `json:"definition,omitempty"`
// WorkerType is a worker node's Worker Type ("rest", "mail", …). Never its
// endpoint and never its credential reference — see [Worker].
WorkerType string `json:"workerType,omitempty"`
// The ArchiMate element bound to this node, when a model binds one. ModelName is
// carried beside Name rather than replacing it: the architect's name and the
// Atlas name are allowed to differ, and the difference is informative.
ModelElementID string `json:"modelElementId,omitempty"`
ModelElementType string `json:"modelElementType,omitempty"`
ModelName string `json:"modelName,omitempty"`
// State is the observation state behind Severity (ADR-0189 §6), kept beside it
// because the three classes are a reading aid and never a replacement: an
// operator acting on a finding needs the state, not the color.
State string `json:"state"`
Severity string `json:"severity"`
// Reason is the sentence behind this node's severity, in the words of whatever
// observed it. Empty on a node with nothing to report.
Reason string `json:"reason,omitempty"`
// SeverityFrom names the descendant a node inherited its severity from, and is
// empty when the severity is the node's own. ADR-0211 §4 requires it: a red
// parent that cannot say which child is red is not actionable.
SeverityFrom string `json:"severityFrom,omitempty"`
// Holds is how many nodes a domain's own landscape holds (ADR-0402 §2), and zero on
// every other kind. It is what makes the estate's budget legible: a domain standing
// for four hundred nodes and one standing for four are the same size on the picture,
// and only this says they are not.
Holds int `json:"holds,omitempty"`
// DrawnBy names the credential whose reach produced a domain's answer (ADR-0402 §1),
// and is empty on every other kind and on this runtime's own domain. A federated read
// is as wide as the credential that made it, and the picture states that rather than
// leaving a reader to discover it.
DrawnBy string `json:"drawnBy,omitempty"`
// Restricted is how many of a domain's [Holds] nodes stood in as placeholders for
// resources the credential that drew it may not see. Set on a domain node only,
// and read against that node rather than against the picture: [Graph.Restricted]
// counts what *this* reader may not see in *this* graph, and this counts what
// somebody else's credential could not see in somebody else's.
//
// Keeping the two apart is the whole point of ADR-0402 §1's disclosure rule. A
// federated subgraph is as wide as the credential that fetched it (ADR-0410), so a
// domain whose reach covered half of it has to say so beside the count — an
// incompleteness that is stated is a fact, one that is not is a discovery.
Restricted int `json:"restricted,omitempty"`
// Incidents is how many unresolved incidents the engine holds against this node.
// Only a process node can carry one — an incident belongs to a token, and only a
// process has tokens — so it is absent everywhere else rather than zero, because
// "no incidents" and "cannot have incidents" are different facts.
//
// It is a count, not a severity: the state above already says what class this
// node is in, and this says how much of it there is. A node with a count is
// always in a state that reports one, so the two can never disagree.
Incidents int `json:"incidents,omitempty"`
// OldestIncident is when the earliest unresolved incident on this node was
// raised, as Unix nanoseconds. It is the one number that says *how long this has
// been wrong*, which is the question a count cannot answer: four hundred
// incidents raised in the last five minutes is a worker that has just fallen
// over, and three standing since Friday is a process nobody is coming back to.
//
// The oldest rather than the newest, and rather than an average. The newest says
// only that something happened lately, which Runtime.LastActivity already says
// better; an average is not a fact about any incident, so nothing can be pointed
// at. The oldest is the age of the *problem* — a process where one token parked
// on Friday and three hundred piled up behind it has been stuck since Friday.
//
// A collapsed application carries the earliest of the processes it stands for,
// by the same argument its summed count makes: it is standing in for them.
//
// Absent rather than zero where there is nothing to date, which includes an
// incident raised before the engine recorded the moment: "not known" and "raised
// at the epoch" are different facts and only one of them is drawable.
OldestIncident int64 `json:"oldestIncident,omitempty"`
// Sites are where in the process that work is parked (see [IncidentSite]).
//
// Only a process node carries them, and a collapsed application deliberately
// does not: an element id without the process it belongs to is not something
// anybody can act on, and a list of them from six different processes would read
// as one broken diagram. The collapsed node keeps the summed count, which is the
// part that survives losing the context.
Sites []IncidentSite `json:"sites,omitempty"`
// Children is how many nodes a collapsed application stands for. Set only when
// the graph is clustered.
Children int `json:"children,omitempty"`
// Runtime is the engine's instance tally for this node (see [Runtime]).
//
// A process node carries its own. A *collapsed* application carries the sum of
// the processes it stands for, for the same reason it carries their summed
// incident count: it is standing in for them, and reporting one child's number
// against the whole would understate it. An uncollapsed application carries
// none — its processes are on screen with their own — and no other kind can have
// instances at all, which is why this is absent rather than zero.
Runtime *Runtime `json:"runtime,omitempty"`
}
Node is one vertex. A restricted node carries no Name, no process id, and no application: its kind is the whole of what it discloses.
type Notation ¶
type Notation struct {
ID string `json:"id"`
Label string `json:"label"`
Short string `json:"short"`
Projection bool `json:"projection"`
MappingVersion int `json:"mappingVersion"`
Types map[string]NotationType `json:"types"`
// Relations is the same for the edges between those kinds. A kind absent from it
// is drawn as the plain derived line and named in the loss list, exactly as an
// absent node kind keeps its derived outline.
Relations map[string]NotationRelation `json:"relations"`
Loss []string `json:"loss"`
}
Notation is one vocabulary the landscape can be drawn in.
Types maps a mesh node kind to what this notation calls it. A kind that is *absent* from the map has no counterpart, and that absence is deliberate: the node keeps its derived shape and the loss list names it. Inventing a row would be the silent drop §8's theme ban exists to prevent.
func NotationByID ¶
NotationByID resolves a notation id. An unknown id is a stale saved view or a hand-edited URL, and the derived vocabulary is the answer that cannot mislead.
type NotationRelation ¶ added in v0.7.0
type NotationRelation struct {
Name string `json:"name"`
Type string `json:"type,omitempty"`
Flip bool `json:"flip,omitempty"`
}
NotationRelation is what one notation calls one derived edge kind, and which way round it runs.
The same one-row rule as NotationType, for the same reason and one more: the edge is now drawn as well as written. A picture that put Triggering's filled arrowhead on an edge the exported document calls Serving would be two answers to one question, and the reader would have no way of telling which was the true one.
Flip says the notation's relationship runs opposite to the derived edge. It is a fact about the vocabulary rather than about the drawing — ArchiMate's Serving runs from the provider to the consumer and the landscape's `uses` edge runs from the consumer to the provider — so it belongs in this table, and the export and the canvas each read it rather than each deciding it.
type NotationType ¶
NotationType is what one notation calls one mesh kind.
Two fields because the two readers need different things from the same row. Name is what a person is shown — "Application Component", spaced the way the standard prints it — and Type is the notation's own machine token, which for ArchiMate is the xsi:type an exchange document must carry and for C4 does not exist, because C4 has no interchange format here. One row, so a picture and a file can never call the same node two different things.
type Observation ¶
type Observation struct {
ElementID string `json:"elementId"`
ElementType string `json:"elementType"`
Key string `json:"key"`
Value string `json:"value"`
Source string `json:"source"`
// State is one of ADR-0189 §6's seven; Severity is ADR-0211 §4's three-class
// reading of it. Both travel: the class is what makes a hundred elements
// legible at a glance, and the state is what somebody acts on.
State string `json:"state"`
Severity string `json:"severity"`
Reason string `json:"reason,omitempty"`
// ObservedAt is when this was read, in Unix seconds. It is on every observation
// rather than only on the document, because the next slice resolves some of
// them from remote nodes and those will not share one instant.
ObservedAt int64 `json:"observedAt"`
Detail map[string]string `json:"detail,omitempty"`
// ChangedAt and PreviousState are what the drift journal knows about this
// value's last transition (ADR-0189 P5). They are on the observation rather
// than only in the journal because "degraded" and "degraded since nine this
// morning" are different findings, and the second is the one somebody acts on.
// Both are absent when nothing has been seen to change — which is not the same
// as nothing having changed, and is why the journal publishes its limits.
ChangedAt int64 `json:"changedAt,omitempty"`
PreviousState string `json:"previousState,omitempty"`
}
Observation is one bound value with what the server currently sees of it.
type ObservationDocument ¶
type ObservationDocument struct {
ContractVersion int `json:"contractVersion"`
// ObservedAt is when this document was computed. Every observation in it was
// read while serving this request — nothing here is cached — which is what
// makes one timestamp meaningful for the whole document today.
ObservedAt int64 `json:"observedAt"`
Observations []Observation `json:"observations"`
Summary ObservationSummary `json:"summary"`
// It is empty now that peers are asked: every one of ADR-0189 §6's states is
// reachable here. The field stays, because an empty list is a claim somebody
// can check — and because the landscape mesh, which draws no deployment
// targets, still has two it cannot produce and publishes them in the same shape.
Unavailable []UnavailableState `json:"unavailable"`
// Problems are the extractor's, carried through exactly as binding resolution
// carries them: a declaration that was refused is as much a finding as one that
// resolved.
Problems []Problem `json:"problems"`
}
ObservationDocument is what a caller asking "what is this model doing" gets.
func Observe ¶
func Observe(set BindingSet, facts Facts, observedAt int64) ObservationDocument
Observe projects the server's facts onto the elements that bind to them.
It never drops a bound value. A binding nothing can say anything about becomes an *unbound* observation naming why, not an absent one — the same rule binding resolution follows, and for the same reason: a model whose elements quietly vanished from the live view looks like a model with nothing wrong.
type ObservationSummary ¶
type ObservationSummary struct {
OK int `json:"ok"`
Attention int `json:"attention"`
Critical int `json:"critical"`
Unknown int `json:"unknown"`
}
ObservationSummary counts the document by severity class, so a listing can say "3 need attention" without walking every element.
type Options ¶
type Options struct {
// Subject is which of the two pictures to derive; empty is the landscape.
Subject string
// Overlays are the Panorama models compared against this landscape. Empty
// leaves the mesh exactly as the derivation alone made it.
Overlays []Overlay
// MaxNodes is the size budget (ADR-0211 §7). Zero means unlimited. Over budget
// the graph collapses to applications and says so, rather than returning a graph
// the browser cannot lay out — or, worse, a truncated one that looks complete.
MaxNodes int
// ObservedAt is when the landscape this graph derives from was read, in Unix
// seconds. Zero omits it, which is what a caller with no clock gets — and is
// deliberately not "now": a derivation that invented a timestamp would be
// claiming a freshness nobody measured.
ObservedAt int64
}
Options tunes one derivation.
type Overlay ¶
type Overlay struct {
ModelID string
ModelName string
Elements []ModelElement
}
Overlay is what one Panorama model contributes to the mesh.
type OverlayCollector ¶
Mesh serves Panorama's derived landscape altitude (ADR-0211). It is a separate service from Service deliberately: Service owns stored ArchiMate documents, Mesh owns a projection that is never stored, and conflating declared intent with derived fact is the thing both records exist to prevent. OverlayCollector supplies the Panorama models compared against the landscape, already filtered for this caller. Like LandscapeCollector it runs on the API run loop and must not call Loop.Do recursively. Optional: a nil collector leaves the mesh exactly as the derivation alone makes it.
type Process ¶
type Process struct {
Key uint64
ProcessID string
Name string
Version int32
// RuntimeID is the runtime holding this deployment (ADR-0401 §1). Empty means the
// runtime deriving the graph, which is every process a local collector reads — a
// server does not have to tell itself where it is.
RuntimeID string
ApplicationID string
CanView bool
Calls []Call
Workers []WorkerUse
Decisions []DecisionUse
// State is the observation state (ADR-0189 §6) the server read for this
// process, and Reason is the sentence behind it. Empty means unbound: no
// observation applies, which is not a finding.
State string
Reason string
// Incidents is how many unresolved incidents the engine holds against this
// definition. It is the count behind Reason rather than a second opinion about
// it, carried as a number so a reader can sort by it and a picture can show
// which of two degraded processes is the worse one.
Incidents int
// OldestIncident is when the earliest of them was raised, as Unix nanoseconds,
// and zero where there are none — or where every one of them predates the field
// (ADR-0272's RaisedAt), which is a fact about the record rather than about the
// process and must not be read as "raised at the epoch".
OldestIncident int64
// Sites are the places in this process where that work is parked.
Sites []IncidentSite
// DeployedAt is when this definition was deployed, in Unix seconds. It is the
// window every traversal count on this process's edges is counted over, because
// the counter is keyed by definition key and a redeploy starts a fresh one.
DeployedAt int64
// ElementVisits is the cumulative activation count of this definition's elements,
// keyed by element index — ADR-0080's maintained counter, read in O(elements).
//
// Nil when the collector could not read it, which is the same distinction Runtime
// makes below and for the same reason: an unread counter and an element nothing has
// reached are different facts, and only one of them is worth saying out loud. An
// element absent from a non-nil map has been read and holds nothing, which is zero.
ElementVisits map[int32]int64
// Runtime is what the engine has recorded about this definition's instances:
// how many are live, how many have finished, and when it last did anything. It
// is nil where the collector could not read it, which is not the same fact as
// zero — a process nothing has ever run and a counter that failed to read are
// different things, and only one of them is worth saying out loud.
Runtime *Runtime
}
Process is one deployed process, with the call activities it makes.
type Product ¶ added in v0.7.0
type Product struct {
ID string
Name string
HomeCatalog string
CanView bool
// ProvisionProcess and DeprovisionProcess are BPMN process ids, which is what a
// product binds: what runs is whatever is deployed when the order line is
// reached, so the binding names an id and never a version.
ProvisionProcess string
DeprovisionProcess string
// State is the item's catalogue state as the store spells it — "draft",
// "active", "withdrawn" (see [catalog.State]) — and it is carried rather than
// reduced to a flag.
//
// Three values and not a boolean, because the two that are not active are not
// the same thing and a reader narrowing by this is usually asking about
// precisely that difference: a draft is an item somebody is still writing, a
// withdrawn one is an item that was real and was retired. Collapsed into
// "not active" they become one heap, and "what did we retire" — the question
// that sends somebody to this picture in the first place — can no longer be
// asked at all.
State string
// Approval is the item's approval rule kind, as [catalog.ApprovalKind] spells
// it: "none", "fixed", "role", "superior", or the name a registered process is
// known by. Empty from a store that has no rule on the item.
//
// The kind and not a "needs approval" boolean, for the reason above and one
// more: the set is open — a fifth kind is modelled and registered under a name
// rather than built in — so the fact is a string this package must not try to
// enumerate. Whether that adds up to "requires approval" is a reading, and a
// reading belongs where the picture is drawn.
Approval string
}
Product is one catalogue item: what is offered, and the two processes that make it real. CanView follows the item's home catalogue (ADR-0315), which is the same rule the product listing applies — a product is edited through one catalogue, and that is the catalogue that decides who may read it.
type ProductCatalog ¶ added in v0.7.0
type ProductCatalog struct {
ID string
Name string
CanView bool
// Items are the products this catalogue offers, by id.
Items []string
Edges []CatalogEdge
}
ProductCatalog is one service catalogue as the mesh sees it (ADR-0312): what it offers, and how what it offers is arranged.
The arrangement lives on the catalogue rather than on the products because it *is* the catalogue's: the same laptop is ordered with a docking station in one catalogue and alone in another, and neither catalogue is wrong. Two catalogues that say different things about one pair therefore produce two edges between one pair of nodes, which is the fact rather than a defect in the drawing.
type ReachOut ¶
ReachOut completes a landscape with what only a call outside this process can tell it: whether the peers this server can promote to are answering.
It is a closure the collector hands back rather than a second collector, and that shape is the whole point. Whatever it needs from the run loop — the target list, the credential each one presents — it captured while it was *on* the loop; what it does with them happens off it. A remote call must never hold the single writer (I3), and a landscape view that could is a landscape view that stops every other design-time request while somebody's peer times out.
It never fails. A peer that cannot be reached is a finding about that peer, not an error about the landscape: one server rebooting must not blank a picture of four hundred nodes.
type Refusal ¶
Refusal is why a connection may not be drawn, in a form a canvas can show.
func MayConnect ¶
MayConnect reports whether this subset will author a relationship of this type between these two element types, and why not when it will not.
Both the canvas and the write path call it, which is the point: a rule that lives in one place cannot disagree with itself.
func MayConnectElements ¶
func MayConnectElements(relationship, sourceID, sourceType, targetID, targetType string) (bool, *Refusal)
MayConnectElements is MayConnect with the two concrete elements, which is what a canvas actually has. It adds the one rule that is about identity rather than type: nothing is related to itself.
type RelationshipKind ¶
type RelationshipKind struct {
Type string `json:"type"`
Label string `json:"label"`
// Rule is the sentence from the specification this implements, in plain words.
// It travels to the browser so a refusal can say *why* rather than only that it
// was refused: "you cannot draw that" teaches nobody the notation.
Rule string `json:"rule"`
}
RelationshipKind is one authorable relationship type.
func DrawableRelationships ¶
func DrawableRelationships() []RelationshipKind
DrawableRelationships returns the relationship subset, in menu order.
type Resolution ¶
type Resolution struct {
ContractVersion int `json:"contractVersion"`
Bindings []ResolvedBinding `json:"bindings"`
// Unresolved counts values that are forbidden or missing. It is the number a
// listing shows as a badge: a model whose bindings no longer resolve is drifting
// from the instance it describes, and that is worth seeing without opening it.
Unresolved int `json:"unresolved"`
// Problems are the extractor's, carried through: a caller needs to hear about
// declarations that were refused as much as about the ones that stood.
Problems []Problem `json:"problems"`
}
Resolution is what a caller asking "what is bound in this model" gets.
func ResolveBindings ¶
func ResolveBindings(set BindingSet, catalog Catalog) Resolution
ResolveBindings resolves every value in a set against the catalog.
It never drops a value. ADR-0189 §4 requires a missing or inaccessible resource to stay an explicit unresolved binding: removing it would make a broken binding look like an absent one, and the model would then look correct.
type ResolvedBinding ¶
type ResolvedBinding struct {
ElementID string `json:"elementId"`
ElementType string `json:"elementType"`
Key string `json:"key"`
Values []ResolvedValue `json:"values"`
}
ResolvedBinding is one key on one element, with every value it binds.
type ResolvedValue ¶
type ResolvedValue struct {
Value string `json:"value"`
Status string `json:"status"`
Name string `json:"name,omitempty"`
}
ResolvedValue is one bound id with what the server could say about it. Name is present only when the caller may see the resource.
type ResourceRef ¶
ResourceRef is one Atlas resource as the resolver sees it. CanView is decided by the server against the caller's sharing scope before resolution runs; this package honors that decision and never makes one.
type Runtime ¶
type Runtime struct {
// Running is how many instances of this definition are live right now.
Running int `json:"running"`
// Finished is how many have completed or been terminated. It never decreases —
// there is no un-finishing — so it is a lifetime total rather than a rate.
Finished int `json:"finished"`
// LastActivity is the Unix-nano timestamp of the most recent lifecycle event of
// any of its instances, or zero for a definition nothing has ever run. Zero is
// meaningful here in a way it is not for the counts: no activity ever is exactly
// what a definition nobody has started reports.
LastActivity int64 `json:"lastActivity,omitempty"`
}
Runtime is a process definition's instance tally (ADR-0083's summary columns, on the Starmap).
Every field is an O(1) point read of a counter the engine already maintains, which is why this can be on every node of a four-hundred-node picture: the alternative — counting instances per definition — is one scan each, and on an instance with a few hundred processes that is the difference between a view and an outage. It is the same argument the parked-work tally already makes for being collected once.
type Service ¶
type Service struct {
// Limits are the installation's resource budgets. New sets them to
// [limits.Default]; the server overwrites them with its own once it has read the
// environment, so every ceiling in this service is the one operators configured
// (ADR-0291).
Limits limits.Limits
// contains filtered or unexported fields
}
Service serves the Panorama model-library HTTP area (ADR-0189). Its only mutable dependency is Store, and every read and write of that store goes through loop, preserving the design-time single-writer boundary.
func New ¶
func New(loop *runloop.Loop, store *Store, access AccessResolver, newID IDGenerator, now Clock, catalog CatalogResolver, facts FactsResolver) *Service
New builds a Panorama service over a run-loop-owned store.
func (*Service) CountForApplicationOnLoop ¶
CountForApplicationOnLoop counts models owned by one application. It is a composition-root hook for application summaries and deletion guards; callers must already be executing on the Service's run loop, as the name makes explicit, so it deliberately does not call Loop.Do recursively.
func (*Service) CountsByApplicationOnLoop ¶
CountsByApplicationOnLoop returns the artifact-count contribution Panorama makes to every application. It has the same run-loop precondition as CountForApplicationOnLoop and performs one store scan for a whole listing.
func (*Service) DeleteForApplicationOnLoop ¶ added in v0.18.0
DeleteForApplicationOnLoop removes every model one application owns — the purge of that application (ADR-0468). A model is owned strictly (ADR-0189), so it goes with its application rather than being left without one. Same run-loop precondition as CountForApplicationOnLoop; idempotent.
func (*Service) HandleAddElement ¶
func (s *Service) HandleAddElement(w http.ResponseWriter, r *http.Request)
HandleAddElement creates an element and places it on a view.
func (*Service) HandleAddRelationship ¶
func (s *Service) HandleAddRelationship(w http.ResponseWriter, r *http.Request)
HandleAddRelationship creates a relationship and draws it on a view.
The subset refuses what ArchiMate does not permit, in the matrix's own words, so a caller that went around the canvas gets the same answer the canvas would have given — and learns the rule rather than only that it was refused.
func (*Service) HandleBindingCandidates ¶
func (s *Service) HandleBindingCandidates(w http.ResponseWriter, r *http.Request)
HandleBindingCandidates lists the resources this caller may bind one key to.
ADR-0189 §4 asks the editor to let an authorized user select only resources they may see; this is that list. It is derived from the same catalog resolution uses, so a picker cannot offer something resolution would then call forbidden.
func (*Service) HandleBindings ¶
func (s *Service) HandleBindings(w http.ResponseWriter, r *http.Request)
HandleBindings resolves every Atlas binding a model declares.
func (*Service) HandleC4 ¶
func (s *Service) HandleC4(w http.ResponseWriter, r *http.Request)
HandleC4 serves the C4 projection of one model.
Read-only by construction: there is no write counterpart and there never will be one under this record. ArchiMate stays the only authored notation (ADR-0189 §7).
func (*Service) HandleContext ¶
func (s *Service) HandleContext(w http.ResponseWriter, r *http.Request)
HandleContext answers "has it been like this" for one element (ADR-0189 P5b).
It writes nothing and stores nothing — the stores it reads are external, owned by somebody else, and retained on their own terms. That is the whole point: ADR-0189 rejected copying remote metrics and logs into a Panorama database by name, so this asks the question every time rather than keeping the answer.
func (*Service) HandleCreate ¶
func (s *Service) HandleCreate(w http.ResponseWriter, r *http.Request)
HandleCreate imports an Open Exchange document into an application-owned Panorama model. The XML is validated but stored byte-for-byte.
func (*Service) HandleDelete ¶
func (s *Service) HandleDelete(w http.ResponseWriter, r *http.Request)
HandleDelete removes a model. A missing or inaccessible model is a 404 so an application scope the caller cannot see is not disclosed.
func (*Service) HandleDrift ¶
func (s *Service) HandleDrift(w http.ResponseWriter, r *http.Request)
HandleDrift returns what has been seen to change about one model, newest first.
It is a separate route from the observations because the two answer different questions and cost different things: "what is happening" needs the engine read, "what changed" is a read of what previous answers already established. A caller watching a landscape asks the first often and the second rarely.
func (*Service) HandleGet ¶
func (s *Service) HandleGet(w http.ResponseWriter, r *http.Request)
HandleGet returns one model's metadata without its XML.
func (*Service) HandleList ¶
func (s *Service) HandleList(w http.ResponseWriter, r *http.Request)
HandleList lists metadata for every model the caller may view. XML is loaded from disk as part of the sidecar record but is never copied into the response.
func (*Service) HandleObservations ¶
func (s *Service) HandleObservations(w http.ResponseWriter, r *http.Request)
HandleObservations projects a model's Atlas bindings onto what this server currently sees (ADR-0189 §6).
It is a read of the model plus a read of the engine, and it writes nothing: the stored document is not touched, and no observation is persisted. That is the rule the record states as "the declarative XML is never mutated by polling", and the reason this is a separate route rather than a field on the model — a caller who wants the drawing must be able to have it without paying for a scan of every live instance.
func (*Service) HandleSetBinding ¶
func (s *Service) HandleSetBinding(w http.ResponseWriter, r *http.Request)
HandleSetBinding sets one binding key on one element, replacing whatever that key held. An empty values list clears it. Everything else in the document — Atlas's other keys, foreign properties, formatting, unsupported standard content — is left byte-for-byte alone.
func (*Service) HandleSetLayout ¶
func (s *Service) HandleSetLayout(w http.ResponseWriter, r *http.Request)
HandleSetLayout writes the new positions of shapes on a view (ADR-0189 §2, P2a).
It takes the same rights as any other write to the model, and the same revision check: two people arranging one view is exactly the case where a lost update would be invisible — the loser's boxes simply drift back, with nothing to say why.
func (*Service) HandleSubset ¶
func (s *Service) HandleSubset(w http.ResponseWriter, _ *http.Request)
HandleSubset serves the authoring subset: what may be created, what may be drawn between what, and the statement that it is a subset (ADR-0189 §2, P2b).
It is served rather than duplicated in the browser, and that is the whole reason this route exists. The canvas has to refuse a connection while it is being dragged; the server has to refuse it on write. Two copies of a relationship matrix is how you get a canvas that lets somebody draw an arrow the server then rejects — so there is one table, and the browser is given it.
It reads no model and takes no id: the subset is a property of this build, not of anybody's document, and asking for it discloses nothing about what exists.
func (*Service) HandleUpdate ¶
func (s *Service) HandleUpdate(w http.ResponseWriter, r *http.Request)
HandleUpdate saves a new revision when expectedRevision still matches. The check and atomic save happen in one run-loop turn, so two browser sessions cannot both overwrite the same revision successfully.
func (*Service) HandleValidate ¶
func (s *Service) HandleValidate(w http.ResponseWriter, r *http.Request)
HandleValidate checks an Open Exchange XML document without storing it.
func (*Service) HandleXML ¶
func (s *Service) HandleXML(w http.ResponseWriter, r *http.Request)
HandleXML exports the canonical Open Exchange document unchanged.
func (*Service) ModelsForApplicationOnLoop ¶ added in v0.18.0
ModelsForApplicationOnLoop lists the models one application owns, newest first, for a deletion preview. Same run-loop precondition as CountForApplicationOnLoop.
func (*Service) OverlaysOnLoop ¶
OverlaysOnLoop returns every model this caller may view, as overlays for the landscape mesh (ADR-0211 §11). It is a composition-root hook like the counting helpers: callers must already be executing on the Service's run loop, as the name says, so it deliberately does not call Loop.Do recursively.
A model whose XML no longer parses is skipped rather than failing the whole overlay: one broken document must not blank out the comparison for every other model. Its bindings are simply not there to compare, which the mesh already has a vocabulary for.
func (*Service) WithContextResolver ¶
func (s *Service) WithContextResolver(resolve ContextResolver) *Service
WithContextResolver wires the historical-context adapters onto a service.
It is a separate call rather than another parameter of New because it is genuinely optional: a server with no log store and no metrics store is a complete Atlas, and its Panorama answers every other question exactly as before.
type Status ¶
type Status struct {
OK int `json:"ok"`
Attention int `json:"attention"`
Critical int `json:"critical"`
// Unknown counts the nodes with no applicable observation. It is stated rather
// than folded into ok, because "nothing is wrong here" and "nothing here is
// watched" are the two answers a status view must never merge.
Unknown int `json:"unknown"`
// Partial reports that the incident scan hit its bound before it finished, so
// some parked work went uncounted and a node's ok is a floor rather than a
// verdict.
Partial bool `json:"partial,omitempty"`
Unavailable []UnavailableState `json:"unavailable"`
}
Status summarizes the severity of one derived mesh.
type Store ¶
Store persists Panorama models as atomic, fsynced design-time sidecars. It is owned by the API run loop and performs no locking of its own (ADR-0189/I3).
type Subset ¶
type Subset struct {
Version int `json:"version"`
Elements []ElementKind `json:"elements"`
Relationships []RelationshipKind `json:"relationships"`
// Matrix is the permitted relationship types for every ordered pair of element
// types in the subset, keyed "Source>Target". It is precomputed and sent whole
// rather than asked for pair by pair: the canvas needs an answer during a drag,
// and a network round trip per pointer move is not an answer.
Matrix map[string][]string `json:"matrix"`
// Limits says what this subset is not, in the same shape every other Panorama
// answer publishes its limits.
Limits []SubsetLimit `json:"limits"`
}
Subset is the whole contract, as one document for the browser: what may be created, what may be drawn, and the statement that it is a subset.
type SubsetLimit ¶
SubsetLimit is one thing this subset does not do, with the reason.
type Summary ¶
type Summary struct {
ID string `json:"id"`
ApplicationID string `json:"applicationId"`
Name string `json:"name"`
Notation string `json:"notation"`
Revision int64 `json:"revision"`
CreatedAt int64 `json:"createdAt"`
CreatedBy string `json:"createdBy,omitempty"`
UpdatedAt int64 `json:"updatedAt"`
UpdatedBy string `json:"updatedBy,omitempty"`
}
Summary is the list/read representation of a Model. The large XML document is intentionally absent and is fetched only through the dedicated export route.
type Target ¶
type Target struct {
ID string
Name string
// State is the observation state ReachOut resolved (ADR-0189 §6), and Reason the
// sentence behind it. Empty means the peer was never asked — which is not the
// same as unreachable, and is what a landscape derived while the loop was closing
// would carry.
State string
Reason string
}
Target is one deployment target and what this server currently knows of it.
It carries no base URL and no credential reference: those are this operator's map of where their infrastructure lives, and a landscape is opened by anybody with modeler access. The name is disclosed to every caller, which is the same rule the binding catalog already applies — a deployment target is org-wide infrastructure with no sharing scope of its own.
No edges are derived to it, and that absence is deliberate rather than pending — but not for the reason this comment used to give. It said a promotion is an act and not a stored relationship, and that this server does not record which of its applications is running over there. It does: `deploymentTarget.Bindings` maps a local application id to the id the same application has on that target, written on the first successful promotion (`api/promote.go`, ADR-0129 option C1) and read back on every later one.
What that map is, precisely, is the record of **a promotion that happened** — not evidence that the application is still deployed there, which nothing local can know. So the join is a fact and the current state is not, which is why the join belongs to the estate altitude that draws exactly that (ADR-0402 §4: the only estate-wide edges that are facts are the application joins a promotion recorded) rather than to this picture, where a line between an application and a target would read as "runs there now". What L0 knows about a target is that the peer exists and whether it answers, and that is what is drawn.
type UnavailableState ¶
type UnavailableState struct {
}
UnavailableState is one observation state this build cannot produce, with the reason it cannot. It is in the payload rather than in the documentation because the consumer of a status picture is the one who needs to know what the absence of a finding is worth.
type ValidationResult ¶
type ValidationResult struct {
Valid bool `json:"valid"`
Notation string `json:"notation"`
Namespace string `json:"namespace,omitempty"`
ModelIdentifier string `json:"modelIdentifier,omitempty"`
Name string `json:"name,omitempty"`
Elements int `json:"elements"`
Relationships int `json:"relationships"`
Views int `json:"views"`
Problems []Problem `json:"problems"`
}
ValidationResult is the bounded summary returned by Panorama validation. Counts describe reusable semantic elements and relationships plus diagram views; they do not duplicate the model into an Atlas-specific metamodel.
func Validate ¶
func Validate(data []byte) ValidationResult
Validate checks the Open Exchange envelope, the standard ArchiMate type vocabulary, global identifier uniqueness, and the references used by relationships and diagram objects. It never rewrites the input: a model that passes is stored and exported byte-for-byte so unsupported-but-standard properties and visual metadata cannot disappear in a round trip.
Full XSD conformance fixtures and the ArchiMate relationship matrix remain a separate interoperability layer. This validator is the safe runtime gate: it is pure Go, bounded, rejects DTD/directive input, and performs no network or external-entity resolution.
type Worker ¶
type Worker struct {
ID string
Name string
Type string
CanView bool
Endpoint string
CredentialsRef string
// State and Reason are this worker's observation, as on [Process].
State string
Reason string
}
Worker is one configured worker as the mesh sees it — a target and identity of a Worker Type (ADR-0203), which is what Type names. Endpoint and CredentialsRef are carried so the derivation can be tested for *not* emitting them: a landscape picture is opened by anyone with modeler access, and an internal hostname is precisely what ADR-0211 §10 keeps out of what leaves the server. Neither field ever reaches a Node.
type WorkerUse ¶
type WorkerUse struct {
ElementID string
// ElementIndex is the same element by the identity the per-element counters are
// keyed on. See [Call.ElementIndex].
ElementIndex int32
Name string
TargetID string
}
WorkerUse is one model reference to a configured worker, already resolved by the server. A model names a worker by name and never carries an endpoint or a secret (ADR-0036/0041), so nothing inside it can tell whether that name is configured anywhere — which is exactly why the references are enumerable from outside (ADR-0158), and why the mesh can answer it. TargetID is empty when no worker by that name is configured.
Source Files
¶
- archimate.go
- bindinghttp.go
- bindingresolve.go
- bindings.go
- bindingwrite.go
- c4.go
- contenthttp.go
- contentwrite.go
- context.go
- contexthttp.go
- drift.go
- estate.go
- layouthttp.go
- layoutwrite.go
- mesh.go
- meshservice.go
- meshstatus.go
- model.go
- notation.go
- observationhttp.go
- observations.go
- service.go
- store.go
- subset.go
- subsethttp.go
- validation.go