Documentation
¶
Overview ¶
Package contract defines the declarative, single-endpoint contract for the admin dashboard: contributor manifests, request/response envelopes, the permission model, the slot/graph composition rules, and the per-contributor version negotiation protocol.
See DESIGN.md in this directory for the spec this implements.
envelope.go
graph.go
manifest.go
registry.go
slots.go
Index ¶
- Constants
- Variables
- func PermissionsHash(user *dashauth.UserInfo) string
- func UnmarshalManifestForTest(b []byte, m *ContractManifest) error
- type Action
- type AuditEmitter
- type AuditFilter
- type AuditRecord
- type AuditStore
- type CacheHint
- type Capability
- type Cardinality
- type ContractManifest
- type Contributor
- type DataBinding
- type Decision
- type Deprecation
- type EnvelopeSupport
- type Error
- type ErrorCode
- type ErrorResponse
- type Extension
- type ExtensionTarget
- type GraphBuilder
- type GraphCache
- type GraphCacheKey
- type GraphNode
- type Intent
- type IntentKind
- type IntentKindDef
- type IntentSchema
- type Kind
- type NavConfig
- type NoopAuditEmitter
- type ParamSource
- type Predicate
- type Principal
- type Query
- type QueryCache
- type Registry
- type RemoteEndpoint
- type Request
- type RequestContext
- type Response
- type ResponseMeta
- type SlotDef
- type StreamEvent
- type SubscriptionMode
- type Warden
- type WardenRegistry
Constants ¶
const MaxSlotDepth = 8
MaxSlotDepth is the maximum nesting depth of graph nodes; trees deeper than this are rejected at registration.
Variables ¶
var ( ErrBadRequest = &Error{Code: CodeBadRequest} ErrUnauthenticated = &Error{Code: CodeUnauthenticated} ErrPermissionDenied = &Error{Code: CodePermissionDenied} ErrNotFound = &Error{Code: CodeNotFound} ErrConflict = &Error{Code: CodeConflict} ErrRateLimited = &Error{Code: CodeRateLimited} ErrUnsupportedVersion = &Error{Code: CodeUnsupportedVersion} ErrInternal = &Error{Code: CodeInternal} )
Sentinel errors for use with errors.Is.
var DefaultSlotCatalog = map[string]IntentKindDef{ "page.shell": { Slots: map[string]SlotDef{ "main": { Accepts: []string{"resource.list", "resource.detail", "dashboard.grid", "form.edit", "audit.tail", "custom", "iframe"}, Cardinality: CardinalityMany, }, }, }, "resource.list": { Slots: map[string]SlotDef{ "rowActions": {Accepts: []string{"action.button", "action.menu", "action.divider"}, Cardinality: CardinalityMany}, "detailDrawer": {Accepts: []string{"form.edit", "resource.detail", "custom"}, Cardinality: CardinalityOne, Extensible: true}, }, }, "dashboard.grid": { Slots: map[string]SlotDef{ "widgets": {Accepts: []string{"metric.counter", "metric.gauge", "audit.tail", "custom"}, Cardinality: CardinalityMany}, }, }, "form.edit": { Slots: map[string]SlotDef{ "fields": {Accepts: []string{"form.field", "custom"}, Cardinality: CardinalityMany, Extensible: true}, }, }, "auth.login.form": {Slots: map[string]SlotDef{}}, }
DefaultSlotCatalog is the v1 catalog of built-in intent kinds and their slots. Adding a new built-in intent kind here is a shell-version bump (adds new renderer behavior). Slice (e) defines the full v1 vocabulary; this map starts with the kinds used by the spec's example.
Functions ¶
func PermissionsHash ¶
PermissionsHash returns a stable, order-independent hash of a user's roles and scopes. Used as part of the graph cache key so that users with the same effective permissions share a cache entry. Claims are NOT included because the contract treats only role/scope as graph-shape-determining.
func UnmarshalManifestForTest ¶
func UnmarshalManifestForTest(b []byte, m *ContractManifest) error
UnmarshalManifestForTest is a test helper exposed for use by sibling packages. It is not part of the package's runtime API; production code should not call it.
Types ¶
type Action ¶
type Action struct {
Contributor string
Intent string
Kind Kind
Capability Capability
Resource map[string]any
}
Action is the operation being authorized. Kind is the wire-side envelope discriminator (graph/query/command/subscribe) so HTTP and SSE callers can pass req.Kind directly. Note this is the wire Kind, not the manifest's IntentKind — the values mostly overlap but "subscription" (manifest) is "subscribe" (wire).
type AuditEmitter ¶
type AuditEmitter interface {
Emit(ctx context.Context, rec AuditRecord)
}
AuditEmitter ships audit records to durable storage. Slice (b) wires the chronicle implementation; slice (a) ships log-based and noop variants.
func NewLogAuditEmitter ¶
func NewLogAuditEmitter(w io.Writer) AuditEmitter
NewLogAuditEmitter returns an emitter that writes a stable line format to w. Suitable for development and as a fallback when no chronicle backend is wired.
func NewRecordingAuditEmitter ¶
func NewRecordingAuditEmitter(inner AuditEmitter, store AuditStore) AuditEmitter
NewRecordingAuditEmitter returns an emitter that fans out to inner (typically the log emitter) and also persists to store. Either may be nil; both nil is a noop.
type AuditFilter ¶
AuditFilter narrows audit.list results. All fields are optional. Limit is clamped to [1, 1000]; zero defaults to 200.
type AuditRecord ¶
type AuditRecord struct {
Time time.Time
Contributor string
Intent string
IntentVersion int
Subject string // resource id when known
User string // user identity (subject from UserInfo)
Result string // ok | error
LatencyMs int64
Payload map[string]any // pre-redaction; subject to per-intent redaction list
CorrelationID string
}
AuditRecord is one auditable command invocation.
type AuditStore ¶
type AuditStore interface {
Append(rec AuditRecord)
List(filter AuditFilter) []AuditRecord
// Subscribe returns a channel that receives every Append from now on, plus
// a cancel func that closes the channel and unregisters the subscriber.
// Slow subscribers drop events rather than block writers — audit is
// telemetry, not the source of truth.
Subscribe() (<-chan AuditRecord, func())
}
AuditStore is the persistent (process-local for slice (k)) view of audit records. It exists to back the audit.list query and audit.tail subscription the dashboard exposes; production deployments swap the in-memory impl for a durable backend when one is wired.
func NewInMemoryAuditStore ¶
func NewInMemoryAuditStore(cap int) AuditStore
NewInMemoryAuditStore returns a store that keeps the most recent `cap` records in a ring buffer. cap <= 0 defaults to 1000.
type CacheHint ¶
type CacheHint struct {
StaleTime string `json:"staleTime,omitempty"`
}
CacheHint communicates how long the shell can serve stale data for a query.
type Capability ¶
type Capability string
Capability is the data-classification of an intent's effects. It composes with IntentKind: a command must be capability=write; a query/subscription must be capability=read; a graph must be capability=render.
const ( CapRead Capability = "read" CapWrite Capability = "write" CapRender Capability = "render" )
type Cardinality ¶
type Cardinality string
Cardinality describes how many fills a slot accepts.
const ( CardinalityOne Cardinality = "one" CardinalityMany Cardinality = "many" )
type ContractManifest ¶
type ContractManifest struct {
SchemaVersion int `yaml:"schemaVersion" json:"schemaVersion"`
Contributor Contributor `yaml:"contributor" json:"contributor"`
Queries map[string]Query `yaml:"queries,omitempty" json:"queries,omitempty"`
Intents []Intent `yaml:"intents" json:"intents"`
Graph []GraphNode `yaml:"graph,omitempty" json:"graph,omitempty"`
Extends []Extension `yaml:"extends,omitempty" json:"extends,omitempty"`
}
ContractManifest is the top-level YAML each contributor publishes.
type Contributor ¶
type Contributor struct {
Name string `yaml:"name" json:"name"`
Envelope EnvelopeSupport `yaml:"envelope" json:"envelope"`
Capabilities []string `yaml:"capabilities,omitempty" json:"capabilities,omitempty"`
}
Contributor names a single contributor and declares its supported envelope versions.
type DataBinding ¶
type DataBinding struct {
QueryRef string `yaml:"-" json:"queryRef,omitempty"`
Intent string `yaml:"intent,omitempty" json:"intent,omitempty"`
Params map[string]ParamSource `yaml:"params,omitempty" json:"params,omitempty"`
}
DataBinding is either an inline {intent, params} pair or a named query reference. YAML supports both shapes:
data: queries.userList
data: { intent: users.list, params: {...} }
func (*DataBinding) UnmarshalYAML ¶
func (d *DataBinding) UnmarshalYAML(value *yaml.Node) error
UnmarshalYAML accepts either a scalar (treated as a named query reference) or a mapping with the inline {intent, params} form.
type Decision ¶
type Decision struct {
// Allow reports whether access is granted.
Allow bool
// Reason is a short, human-readable explanation. Surfaced in audit logs
// and (optionally) in error responses.
Reason string
// Redactions lists JSONPath-like field paths that must be redacted from
// the response payload even when Allow is true. Empty when no redactions
// apply.
Redactions []string
}
Decision is the Warden's verdict.
type Deprecation ¶
type Deprecation struct {
IntentVersion int `json:"intentVersion"`
RemoveAfter string `json:"removeAfter"`
}
Deprecation surfaces a "this version will be removed" hint to the shell.
type EnvelopeSupport ¶
type EnvelopeSupport struct {
Supports []string `yaml:"supports" json:"supports"`
Preferred string `yaml:"preferred" json:"preferred"`
}
EnvelopeSupport declares which envelope versions this contributor can speak.
type Error ¶
type Error struct {
Code ErrorCode `json:"code"`
Message string `json:"message,omitempty"`
Details map[string]any `json:"details,omitempty"`
Retryable bool `json:"retryable,omitempty"`
CorrelationID string `json:"correlationID,omitempty"`
Redactions []string `json:"redactions,omitempty"`
}
Error is the canonical contract error type. It serializes to the wire "error" object documented in DESIGN.md.
type ErrorCode ¶
type ErrorCode string
ErrorCode is a canonical, wire-stable code for contract errors. Contributor-specific codes are namespaced like "auth.SESSION_EXPIRED".
const ( CodeBadRequest ErrorCode = "BAD_REQUEST" CodeUnauthenticated ErrorCode = "UNAUTHENTICATED" CodePermissionDenied ErrorCode = "PERMISSION_DENIED" CodeNotFound ErrorCode = "NOT_FOUND" CodeConflict ErrorCode = "CONFLICT" CodeRateLimited ErrorCode = "RATE_LIMITED" CodeUnsupportedVersion ErrorCode = "UNSUPPORTED_VERSION" CodeInternal ErrorCode = "INTERNAL" )
type ErrorResponse ¶
type ErrorResponse struct {
OK bool `json:"ok"`
Envelope string `json:"envelope"`
Error *Error `json:"error"`
}
ErrorResponse is the wire envelope for failed POST responses.
type Extension ¶
type Extension struct {
Target ExtensionTarget `yaml:"target" json:"target"`
Slot string `yaml:"slot" json:"slot"` // dotted path: "detailDrawer.fields"
Add []GraphNode `yaml:"add" json:"add"`
}
Extension declares that this contributor wants to add nodes into another contributor's slot.
type ExtensionTarget ¶
type ExtensionTarget struct {
Contributor string `yaml:"contributor" json:"contributor"`
Intent string `yaml:"intent" json:"intent"`
Route string `yaml:"route,omitempty" json:"route,omitempty"`
}
ExtensionTarget identifies the host node to extend.
type GraphBuilder ¶
type GraphBuilder struct {
// contains filtered or unexported fields
}
GraphBuilder produces a per-(route, principal) filtered graph by walking the merged graph from the registry and dropping nodes whose visibleWhen predicates fail. EnabledWhen is preserved as an annotation (it does not strip the node); the React shell honors it for disabled-but-visible UI states.
func NewGraphBuilder ¶
func NewGraphBuilder(reg Registry, wardens WardenRegistry) *GraphBuilder
NewGraphBuilder returns a builder bound to the given registry and warden registry.
func (*GraphBuilder) Build ¶
func (b *GraphBuilder) Build(ctx context.Context, contributor, route string, p Principal) (*GraphNode, error)
Build returns the filtered graph rooted at the given route for the given principal. Returns ErrNotFound if no contributor owns the route, or ErrPermissionDenied if the root node itself is filtered out by the principal's permissions.
func (*GraphBuilder) BuildWithParams ¶
func (b *GraphBuilder) BuildWithParams(ctx context.Context, contributor, route string, p Principal) (*GraphNode, map[string]string, error)
BuildWithParams is Build plus the route-pattern params extracted from the matched route. For exact-route matches the map is empty (non-nil); for :name-style routes it carries the placeholder values. Slice (j) added this so the transport handler can surface params in ResponseMeta.
type GraphCache ¶
type GraphCache struct {
// contains filtered or unexported fields
}
GraphCache is a small LRU+TTL cache. Bust on contributor manifest reload.
func NewGraphCache ¶
func NewGraphCache(maxEntries int, ttl time.Duration) *GraphCache
NewGraphCache creates a cache with the given max size and TTL per entry. TTL of 0 disables expiry.
func (*GraphCache) BustAll ¶
func (c *GraphCache) BustAll()
BustAll clears the cache. Call after a contributor manifest reload or shell deploy.
func (*GraphCache) Get ¶
func (c *GraphCache) Get(k GraphCacheKey) (*GraphNode, bool)
func (*GraphCache) Put ¶
func (c *GraphCache) Put(k GraphCacheKey, v *GraphNode)
type GraphCacheKey ¶
GraphCacheKey is the (route, permissionsHash, shellVersion) tuple keyed by the cache.
type GraphNode ¶
type GraphNode struct {
Route string `yaml:"route,omitempty" json:"route,omitempty"` // top-level only
Intent string `yaml:"intent" json:"intent"`
Title string `yaml:"title,omitempty" json:"title,omitempty"`
Root bool `yaml:"root,omitempty" json:"root,omitempty"`
Data *DataBinding `yaml:"data,omitempty" json:"data,omitempty"`
Props map[string]any `yaml:"props,omitempty" json:"props,omitempty"`
Slots map[string][]GraphNode `yaml:"slots,omitempty" json:"slots,omitempty"`
VisibleWhen *Predicate `yaml:"visibleWhen,omitempty" json:"visibleWhen,omitempty"`
EnabledWhen *Predicate `yaml:"enabledWhen,omitempty" json:"enabledWhen,omitempty"`
Op string `yaml:"op,omitempty" json:"op,omitempty"` // for action nodes
Payload map[string]ParamSource `yaml:"payload,omitempty" json:"payload,omitempty"`
Component string `yaml:"component,omitempty" json:"component,omitempty"` // intent: custom escape hatch
Src string `yaml:"src,omitempty" json:"src,omitempty"` // intent: iframe escape hatch
Sandbox []string `yaml:"sandbox,omitempty" json:"sandbox,omitempty"`
Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"`
}
GraphNode is a single node in the UI graph (an intent invocation with slot fills).
type Intent ¶
type Intent struct {
Name string `yaml:"name" json:"name"`
Kind IntentKind `yaml:"kind" json:"kind"`
Version int `yaml:"version" json:"version"`
Capability Capability `yaml:"capability" json:"capability"`
Requires Predicate `yaml:"requires,omitempty" json:"requires,omitempty"`
Schema IntentSchema `yaml:"schema,omitempty" json:"schema,omitempty"`
Mode SubscriptionMode `yaml:"mode,omitempty" json:"mode,omitempty"` // subscription only
Invalidates []string `yaml:"invalidates,omitempty" json:"invalidates,omitempty"` // command only
Audit *bool `yaml:"audit,omitempty" json:"audit,omitempty"` // default true for commands
Deprecated *Deprecation `yaml:"deprecated,omitempty" json:"deprecated,omitempty"`
}
Intent declares a single named operation and its security/version metadata.
type IntentKind ¶
type IntentKind string
IntentKind is the wire-level discriminator declared on every intent. It must be consistent with the request envelope Kind at dispatch time.
const ( IntentKindGraph IntentKind = "graph" IntentKindQuery IntentKind = "query" IntentKindCommand IntentKind = "command" IntentKindSubscription IntentKind = "subscription" )
type IntentKindDef ¶
IntentKindDef declares the slots of a built-in intent kind.
type IntentSchema ¶
type IntentSchema struct {
Input map[string]any `yaml:"input,omitempty" json:"input,omitempty"`
Output any `yaml:"output,omitempty" json:"output,omitempty"`
}
IntentSchema is loose by design: contributors describe their input/output shapes; validation against this is opt-in (slice (b) wires it).
type Kind ¶
type Kind string
Kind discriminates request/response semantics on the wire. A kind is enforced against the intent's declared Capability at dispatch time.
type NavConfig ¶
type NavConfig struct {
}
NavConfig is per-route nav metadata; mirrors today's contributor.NavItem fields.
type NoopAuditEmitter ¶
type NoopAuditEmitter struct{}
NoopAuditEmitter is the disabled-audit implementation.
func (NoopAuditEmitter) Emit ¶
func (NoopAuditEmitter) Emit(_ context.Context, _ AuditRecord)
type ParamSource ¶
type ParamSource struct {
Value any `yaml:"value,omitempty" json:"value,omitempty"`
From string `yaml:"from,omitempty" json:"from,omitempty"` // route.X | parent.X | state.X | session.X
}
ParamSource describes where a parameter value comes from. Exactly one of Value/From is set; YAML uses { from: route.tenant } or a literal.
func (*ParamSource) UnmarshalYAML ¶
func (p *ParamSource) UnmarshalYAML(value *yaml.Node) error
UnmarshalYAML accepts either a scalar (treated as the From source) or a mapping with the explicit {value} or {from} form.
type Predicate ¶
type Predicate struct {
All []string `yaml:"all,omitempty" json:"all,omitempty"`
Any []string `yaml:"any,omitempty" json:"any,omitempty"`
Not []string `yaml:"not,omitempty" json:"not,omitempty"`
Warden string `yaml:"warden,omitempty" json:"warden,omitempty"`
}
Predicate is the boolean access expression: any of all/any/not, plus an optional named Warden delegate. An empty Predicate evaluates to allow.
type Principal ¶
Principal is the caller identity passed to Wardens and the predicate engine.
func PrincipalFor ¶
PrincipalFor builds a Principal from a UserInfo, copying claims for safety.
type Query ¶
type Query struct {
Intent string `yaml:"intent" json:"intent"`
Params map[string]ParamSource `yaml:"params,omitempty" json:"params,omitempty"`
Cache *QueryCache `yaml:"cache,omitempty" json:"cache,omitempty"`
}
Query is a named, reusable, cacheable data binding referenced by graph nodes.
type QueryCache ¶
type QueryCache struct {
StaleTime string `yaml:"staleTime,omitempty" json:"staleTime,omitempty"`
}
QueryCache declares per-query staleness for the client.
type Registry ¶
type Registry interface {
Register(m *ContractManifest) error
Contributor(name string) (*ContractManifest, bool)
Intent(contributor, intent string, version int) (Intent, bool)
HighestVersion(contributor, intent string) (int, bool)
All() []*ContractManifest
MergedGraph(contributor, route string) (*GraphNode, bool)
// MatchRoute is MergedGraph plus :name-style placeholder matching. On a
// match the returned map carries the extracted segment values keyed by
// placeholder name. Exact route matches return an empty (non-nil) map.
// Slice (j) added this for deep-link detail routes; MergedGraph remains
// for callers that don't care about params.
MatchRoute(contributor, route string) (*GraphNode, map[string]string, bool)
// RegisterRemote records a contributor whose handlers live in another
// service. The manifest is registered identically to a local one so the
// graph endpoint and capabilities listing work uniformly; the endpoint
// is what the dispatcher's forwarding layer reads to know where to send
// envelopes. Slice (m) added this.
RegisterRemote(m *ContractManifest, endpoint RemoteEndpoint) error
// IsRemote reports whether the named contributor was registered via
// RegisterRemote.
IsRemote(contributor string) bool
// Remote returns the upstream endpoint for a contributor previously
// registered via RegisterRemote. ok is false for local contributors.
Remote(contributor string) (RemoteEndpoint, bool)
// Unregister removes a contributor and all its intents + merged graph.
// Used by discovery loops to clean up offline remotes; safe to call
// for unknown names.
Unregister(contributor string)
}
Registry holds all registered contributor manifests and provides lookup by (contributor, intent, version) plus highest-active-version queries for negotiation. It also stores per-contributor merged graphs reflecting any cross-contributor slot extensions applied at registration time.
type RemoteEndpoint ¶
type RemoteEndpoint struct {
// BaseURL is the upstream service's root, including any path prefix
// (e.g. https://svc.internal:8443 or /proxied/svc). The forwarding
// client appends "/_forge/contract/dispatch" for envelope POSTs and
// "/_forge/contract/manifest" for manifest fetches.
BaseURL string
// APIKey, when non-empty, is sent as Authorization: Bearer <key> on
// every forwarded envelope so the upstream can authenticate the
// dashboard. Inbound user headers (Authorization, Cookie) are still
// forwarded so the upstream sees the end-user identity too — the
// API key authenticates the dashboard itself; user identity flows in
// parallel.
APIKey string
// Client overrides the http.Client used to talk to this remote.
// nil = a default client with a 10s timeout.
Client *http.Client
}
RemoteEndpoint describes how to reach a contract contributor that lives in another service. Slice (m) introduced this so the dispatcher's forwarding layer knows where to send envelopes for a contributor whose handlers are out-of-process.
type Request ¶
type Request struct {
Envelope string `json:"envelope"`
Kind Kind `json:"kind"`
Contributor string `json:"contributor"`
Intent string `json:"intent"`
IntentVersion int `json:"intentVersion,omitempty"`
Payload json.RawMessage `json:"payload,omitempty"`
Params map[string]any `json:"params,omitempty"`
Context RequestContext `json:"context"`
CSRF string `json:"csrf,omitempty"`
IdempotencyKey string `json:"idempotencyKey,omitempty"`
}
Request is the wire envelope for POST /api/dashboard/{envelope}.
type RequestContext ¶
type RequestContext struct {
Route string `json:"route,omitempty"`
CorrelationID string `json:"correlationID,omitempty"`
}
RequestContext carries route + correlation metadata. Always populated by the shell.
type Response ¶
type Response struct {
OK bool `json:"ok"`
Envelope string `json:"envelope"`
Kind Kind `json:"kind"`
Data json.RawMessage `json:"data,omitempty"`
Meta ResponseMeta `json:"meta"`
}
Response is the wire envelope for successful POST responses.
type ResponseMeta ¶
type ResponseMeta struct {
IntentVersion int `json:"intentVersion,omitempty"`
Deprecation *Deprecation `json:"deprecation,omitempty"`
CacheControl *CacheHint `json:"cacheControl,omitempty"`
Invalidates []string `json:"invalidates,omitempty"`
RouteParams map[string]string `json:"routeParams,omitempty"`
}
ResponseMeta carries cross-cutting metadata (versioning, caching, invalidation).
RouteParams is populated by graph responses for routes that contain :name placeholders (e.g. /traces/:id). The map is keyed by placeholder name and holds the matched URL value. Slice (j) introduced this so the shell can resolve `route.<name>` in payload bindings.
type SlotDef ¶
type SlotDef struct {
Accepts []string // intent names accepted in this slot
Cardinality Cardinality
Extensible bool // if true, other contributors may extend via Extends
}
SlotDef describes one slot of a parent intent kind.
type StreamEvent ¶
type StreamEvent struct {
Intent string `json:"intent"`
Mode SubscriptionMode `json:"mode"`
Payload json.RawMessage `json:"payload"`
Seq uint64 `json:"seq"`
}
StreamEvent is the SSE payload for a single subscription event.
type SubscriptionMode ¶
type SubscriptionMode string
SubscriptionMode is how the client integrates events into local state.
const ( ModeReplace SubscriptionMode = "replace" ModeAppend SubscriptionMode = "append" ModeSnapshotDelta SubscriptionMode = "snapshot+delta" )
type Warden ¶
Warden is the pluggable, data-aware authorization second pass. It runs after the YAML boolean Predicate succeeds and may inspect intent params (e.g. tenant ownership), claims, or external policy.
type WardenRegistry ¶
type WardenRegistry interface {
Register(name string, w Warden) error
Get(name string) (Warden, bool)
}
WardenRegistry maps a Warden's declared name to its implementation. Manifest validation rejects YAML that references a name not in the registry.
func NewWardenRegistry ¶
func NewWardenRegistry() WardenRegistry
NewWardenRegistry returns an empty in-memory registry.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package dispatcher implements transport.Dispatcher and transport.SubscriptionSource against a function-table of registered handlers.
|
Package dispatcher implements transport.Dispatcher and transport.SubscriptionSource against a function-table of registered handlers. |
|
Package idempotency provides command deduplication for the dashboard contract: a Store interface plus an in-memory implementation.
|
Package idempotency provides command deduplication for the dashboard contract: a Store interface plus an in-memory implementation. |
|
validate.go
|
validate.go |
|
Package pilot ships the migrated dashboard contributor used to validate the contract end-to-end: extensions.list, services.list, services.detail, and the metrics.summary subscription, all wired against the existing collector and contributor registry.
|
Package pilot ships the migrated dashboard contributor used to validate the contract end-to-end: extensions.list, services.list, services.detail, and the metrics.summary subscription, all wired against the existing collector and contributor registry. |
|
Package remote implements the contract dispatcher's HTTP forwarding layer.
|
Package remote implements the contract dispatcher's HTTP forwarding layer. |
|
Package server exposes the two HTTP endpoints a non-dashboard service needs to advertise itself as a contract contributor that other dashboards can discover + dispatch into.
|
Package server exposes the two HTTP endpoints a non-dashboard service needs to advertise itself as a contract contributor that other dashboards can discover + dispatch into. |
|
capabilities.go
|
capabilities.go |