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 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 CatalogResolver
- type Clock
- type ContextDocument
- type ContextLimit
- type ContextQuery
- type ContextResolver
- type ContextResult
- type ContextWindow
- type Decision
- type Draft
- type DriftDocument
- type DriftEntry
- type DriftLimit
- type Edge
- type ElementKind
- 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 NotationType
- type Observation
- type ObservationDocument
- type ObservationSummary
- type Options
- type Overlay
- type OverlayCollector
- type Problem
- type Process
- 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) 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) 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 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 = "process" // 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" )
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. EdgeUses = "uses" )
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 ( 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.
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 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 = 1
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.
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 SubsetVersion = 1
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.
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 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 ¶
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
}
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 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 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 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 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"`
// 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 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 {
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
// 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
}
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) 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"`
// ProcessID and Version identify a process node well enough to navigate to the
// Operations view (L2) without a second lookup.
ProcessID string `json:"processId,omitempty"`
Version int32 `json:"version,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"`
// 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"`
// 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"`
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 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 {
// 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
ApplicationID string
CanView bool
Calls []Call
Workers []WorkerUse
Decisions []string
// 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
// Sites are the places in this process where that work is parked.
Sites []IncidentSite
// 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 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 {
// 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) 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) 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. A promotion is an act, not a stored relationship: this server does not record which of its applications is running over there, so any line drawn from one to a target would be an assertion nobody made. What it does know is that the peer exists and whether it answers, and that is exactly 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 ¶
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
- 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