Documentation
¶
Index ¶
- Variables
- type Cache
- type CacheProvider
- type CatalogBlobStore
- type CatalogBlobStoreProvider
- type CatalogPublishOutcome
- type CatalogPublisher
- type CatalogPublisherProvider
- type CatalogSubmission
- type CrawlStatus
- type Crawler
- type CrawlerProvider
- type Decrypter
- type DecrypterProvider
- type Encrypter
- type EncrypterProvider
- type FileRef
- type KeyManager
- type KeyManagerProvider
- type ManifestLoader
- type ManifestLoaderProvider
- type MasterDependency
- type MiddlewareProvider
- type OtelSetupMetricsProvider
- type PayloadEntry
- type PayloadStore
- type PayloadStoreProvider
- type PolicyChecker
- type PolicyCheckerProvider
- type PriorCatalogState
- type PublishError
- type PublishRequest
- type PublishResult
- type Publisher
- type PublisherProvider
- type RegistryLookup
- type RegistryLookupProvider
- type RegistryMetadataLookup
- type ResponseStep
- type RetiredCatalogFile
- type RetirementOutcome
- type Router
- type RouterProvider
- type SchemaValidator
- type SchemaValidatorProvider
- type SchemaVersionMediator
- type SchemaVersionMediatorProvider
- type SignValidator
- type SignValidatorProvider
- type Signer
- type SignerProvider
- type Step
- type StepProvider
- type Translator
- type TranslatorProvider
- type TransportWrapper
- type TransportWrapperProvider
Constants ¶
This section is empty.
Variables ¶
var ErrBlobNotFound = store.ErrBlobNotFound
ErrBlobNotFound is the sentinel a CatalogBlobStore.Get implementation must return when path has never been written. It's the same sentinel catalog-core's store.Store checks for (via errors.Is) to tell "nothing here yet, start fresh" apart from a real backend failure -- aliased here rather than redeclared so every onix CatalogBlobStore plugin stays compatible with it without importing catalog-core directly.
Functions ¶
This section is empty.
Types ¶
type Cache ¶
type Cache interface {
// Get retrieves a value from the cache based on the given key.
Get(ctx context.Context, key string) (string, error)
// Set stores a value in the cache with the given key and TTL (time-to-live) in seconds.
Set(ctx context.Context, key, value string, ttl time.Duration) error
// Delete removes a value from the cache based on the given key.
Delete(ctx context.Context, key string) error
// Clear removes all values from the cache.
Clear(ctx context.Context) error
}
Cache defines the general cache interface for caching plugins.
type CacheProvider ¶
type CacheProvider interface {
// New initializes a new cache instance with the given configuration.
New(ctx context.Context, config map[string]string) (Cache, func() error, error)
}
CacheProvider interface defines the contract for managing cache instances.
type CatalogBlobStore ¶ added in v1.9.0
type CatalogBlobStore interface {
// Get returns the bytes stored at path, or ErrBlobNotFound if nothing
// has been written there yet.
Get(ctx context.Context, path string) ([]byte, error)
// Put writes content at path, creating or overwriting it. Every path
// catalogstore.Store passes is already content/version-addressed (a
// versioned filename, or the fixed "latest"/index path), so a
// backend needs no versioning or locking of its own beyond an atomic
// per-path overwrite.
Put(ctx context.Context, path string, content []byte) error
}
CatalogBlobStore is the only backend-specific capability a catalog storage backend needs: read/write bytes at a path. It carries no catalog-file vocabulary at all -- every backend (local disk, S3, GCS, git, an authenticated CDN write root) implements exactly this and nothing more. catalogstore.Store is the one shared layer that understands how a catalog index, its baseline, change files, and "latest" pointer fit together, built on top of whichever CatalogBlobStore is configured -- that understanding is common across every backend, so it is deliberately not part of this interface.
type CatalogBlobStoreProvider ¶ added in v1.9.0
type CatalogBlobStoreProvider interface {
New(ctx context.Context, config map[string]string) (CatalogBlobStore, func() error, error)
}
CatalogBlobStoreProvider is the plugin constructor interface.
type CatalogPublishOutcome ¶ added in v1.9.0
type CatalogPublishOutcome struct {
CatalogID string
// SignedEntry is this catalog's complete, already-signed catalogs[]
// entry -- ready to hand a catalog storage layer (e.g.
// pkg/catalog/store's CatalogUpdate.SignedEntry) to merge into the
// index. Publish itself never assembles or persists the index as a
// whole; it only ever produces this one entry's bytes.
SignedEntry json.RawMessage
// Version is this catalog's new current file-lineage version after
// this call (the version stamped on the file just published, or the
// unchanged current version on a no-op/metadata-only change) --
// distinct from EntryVersion below (NFH-014 §Versioning).
Version int
// EntryVersion is this catalog's new entry-level version -- bumped
// whenever Changed is true, whether that's a content change (Mode
// "baseline"/"change") or a metadata-only one (Mode "metadata").
// Callers must carry this forward into the next call's
// PriorState[catalogId].EntryVersion.
EntryVersion int
Changed bool // false = no-op: diffed against PriorState and found no changes at all, content or metadata
Digest string
// Mode is "baseline" (fresh full-file publish, including a forced
// compaction), "change" (a diffed delta was produced), "metadata" (no
// file republished, but NetworkIds/SchemaTypes/CatalogType/IsActive/
// Dependencies/CrawlHint changed, so EntryVersion still bumped), or
// "unchanged". Content holds the new file's canonical (never
// compressed) bytes for "baseline"/"change" and is nil otherwise --
// digest/signature verification and any programmatic inspection always
// use this, never ServedContent.
Mode string
Content json.RawMessage
// ServedContent is what a caller should actually write to storage for
// Content above -- identical to Content when Compressed is false, or
// its gzip-compressed bytes when Compressed is true (NFH-014 §10.1,
// "Compression"). Nil whenever Content is nil.
ServedContent []byte
// Compressed reports whether ServedContent (and LatestServedContent
// below) are gzip-compressed relative to Content/LatestContent --
// mirrors Config.Gzip at the time of this Publish call. The index
// entry's own file reference URLs already carry the matching ".gz"
// extension; a caller writing ServedContent to a filename derived from
// that URL needs no separate bookkeeping.
Compressed bool
// LatestContent/LatestServedContent/LatestDigest are set whenever
// Config.PublishLatest is on (NFH-014 §Schema Changes, "latest"): a
// full CatalogFile mirroring this catalog's current content,
// regenerated on every call regardless of Mode -- a caller writes
// LatestServedContent to the same fixed, overwritten-in-place URL
// every time (never a new, versioned one like Content above). Nil/
// empty when PublishLatest is off.
LatestContent json.RawMessage
LatestServedContent []byte
LatestDigest string
}
CatalogPublishOutcome reports what happened to one submitted catalog.
type CatalogPublisher ¶ added in v1.9.0
type CatalogPublisher interface {
Publish(ctx context.Context, req PublishRequest) (PublishResult, error)
// DecodeRequest parses and validates an inbound HTTP request into a
// PublishRequest: method check, body read, wire-format JSON decoding,
// referential/business validation, and any request-shape-specific
// schema checks (e.g. NFH-014's schemaTypes). Any error here means the
// request itself is malformed or invalid, surfaced by the generic
// handler as a transport-level 400 -- never a partial/business failure
// (that's what Publish's own PublishError/Warnings are for).
DecodeRequest(ctx context.Context, r *http.Request) (PublishRequest, error)
// IndexURL returns the public location this publisher's catalog index
// is (or will be) reachable at -- callers use this to check the
// publisher's own DeDi registry record for a matching
// meta.catalog_index_url before publishing (see
// pkg/plugin/implementation/catalogpublisher/registrylink.go), without
// this package knowing anything about DeDi or registries itself.
IndexURL() string
}
CatalogPublisher turns a publisher's catalog submissions into a catalog index whose file entries carry their own signatures. It is the producing side of the chain definition.Crawler consumes and verifies.
Publish now owns the full publish operation end to end, not just the diffing/signing math: it loads this catalog's prior state from its own configured CatalogBlobStore, delegates diffing/signing/versioning to pkg/catalog/publisher, persists the result back to that same storage backend, and -- if configured -- checks whether this node's DeDi registry record already links its catalog index, surfacing a miss as a PublishResult.Warnings entry rather than failing the call. This replaces the earlier design where Publish was a pure function of (submissions, prior state) -> result with no storage of its own, and a caller (the generic HTTP handler) owned loading/persisting state around it -- that responsibility has moved into the plugin so the handler can stay fully generic.
type CatalogPublisherProvider ¶ added in v1.9.0
type CatalogPublisherProvider interface {
New(ctx context.Context, keyManager KeyManager, blobStore CatalogBlobStore, registry RegistryLookup, registryMetadata RegistryMetadataLookup, config map[string]string) (CatalogPublisher, func() error, error)
}
CatalogPublisherProvider is the plugin constructor interface. blobStore is required (non-nil): Publish always persists what it produces somewhere, so a CatalogPublisher implementation cannot be constructed without one. registryMetadata is optional -- nil when the registry catalog-index-link check isn't configured (or the configured Registry plugin doesn't implement RegistryMetadataLookup); a non-nil value is only ever used to run that check.
type CatalogSubmission ¶ added in v1.9.0
type CatalogSubmission struct {
// CatalogID is the full participant-scoped id, e.g.
// "open-economy.nfh.global/electronics-2026".
CatalogID string
// CatalogType defaults to "REGULAR" when empty (see the file spec's
// catalogType field; "MASTER" is the design doc's still-open
// "MASTER catalogs" case).
CatalogType string
SchemaTypes []string
// NetworkIds scopes this catalog to specific networks; empty/nil means
// public (file spec: "networkIds ... Empty or absent means public").
NetworkIds []string
// Dependencies lists, for a REGULAR catalog, every MASTER catalog any
// of its resources currently extend via
// resourceDirectives[].extends.masterResourceId (NFH-014 §10.3,
// CON-TBD-30). Publish does not derive this from Catalog's own
// resource content -- a masterResourceId names a resource, not the
// catalog it lives in, so resolving which catalog (and which index)
// owns it needs cross-catalog knowledge only the caller has. Nil for a
// catalog with no MASTER dependencies (including every MASTER catalog
// itself).
Dependencies []MasterDependency
// CrawlHint is an optional suggested crawl frequency (NFH-014's
// "comparable to a sitemap's changefreq") a crawler MAY honor. Empty
// means no hint is published.
CrawlHint string
Catalog json.RawMessage
// ForceBaseline bypasses diffing against PriorState and always emits a
// fresh baseline for this one catalog -- a per-catalog control, not a
// batch-wide one (mirroring catalog-core's own
// publisher.Submission.Directives.ForceBaseline): a caller submitting
// several catalogs in one call can force a baseline for one while
// leaving the others to diff normally. For a catalog with no prior
// state this is a no-op (already the default); for one with prior
// state, this is how a caller triggers compaction -- a fresh baseline
// at the next version, discarding the accumulated change list.
ForceBaseline bool
}
CatalogSubmission is one catalog's input to a publish call: the plain Beckn Catalog object (no context/message envelope) plus the publisher- declared metadata that only the publisher can know -- NetworkIds is never derived from the catalog content itself. Catalogs are public, unconditionally (RFC NFH-014, "Catalog access is public") -- there is no restricted-catalog or per-catalog-auth concept; NetworkIds is a Discovery-service relevance filter only, never an access control.
IsActive is deliberately absent here: the index entry's isActive mirrors Catalog's own pre-existing isActive field (NFH-014 §Schema Changes, "Catalog Index"), so Publish reads it out of Catalog itself rather than duplicating it as a second input that could disagree.
type CrawlStatus ¶ added in v1.9.0
type CrawlStatus struct {
CatalogID string `json:"catalogId"`
IndexURL string `json:"indexUrl,omitempty"`
// EverSynced is false for a catalog queued for its very first sync --
// EntryVersion/Retired/LastError/UpdatedAt are all zero-valued in that
// case, since nothing has settled yet. Check this (not EntryVersion
// being zero) to tell "never synced" apart from a genuinely zero
// version, and check Queued to see whether that first sync is in fact
// pending right now.
EverSynced bool `json:"everSynced"`
// EntryVersion is this catalog's last-settled entry-level version (see
// CatalogPublishOutcome.EntryVersion for what it tracks) -- what the
// crawler last successfully applied, which may lag the publisher's
// actual current state if a sync is still queued or retrying (see
// Queued/Attempts/NextAttemptAt below). Deliberately the only version
// reported here, not the file-lineage Version CatalogPublishOutcome
// also carries: EntryVersion is what a publisher already has in hand
// from its own Publish call (RFC NFH-014's entry-level versioning) to
// cross-check the crawler actually caught up, and it bumps on every
// entry change -- content or metadata-only -- unlike Version, which can
// stay unchanged on a metadata-only publish and so would under-report
// staleness here.
EntryVersion int64 `json:"entryVersion"`
// Retired is true once this catalog's index entry carried a tombstone
// (retiredAt) on its last successful sync.
Retired bool `json:"retired"`
// LastError is the most recent sync failure's reason, or empty if the
// catalog has never failed, or last failed before its most recent
// success (a success clears this). Its presence does not by itself mean
// the catalog is out of sync -- check Queued/Attempts too, since a
// once-failed catalog that later succeeded reports LastError empty.
LastError string `json:"lastError,omitempty"`
// Queued is true while a sync for this catalog is actively pending or
// in flight -- Attempts/NextAttemptAt are only meaningful when this is
// true. Parked and Abandoned are separate, mutually exclusive states
// (see their own doc comments below), not folded into Queued.
Queued bool `json:"queued"`
Attempts int `json:"attempts,omitempty"`
NextAttemptAt time.Time `json:"nextAttemptAt,omitempty"`
// Parked is true once a permanent (or retry-budget-exhausted) failure
// parked this catalog -- crawlmanager stops retrying it on its own
// schedule. It isn't necessarily stuck forever: a periodic sweep (see
// ParkCount) may revive it automatically, and publishing a fresh
// version of the catalog reactivates it immediately regardless.
Parked bool `json:"parked,omitempty"`
// ParkCount is how many times this catalog has been parked, ever
// (cumulative -- not reset by a revival). Compared against the
// deployment's own configured park-retry budget to decide whether the
// next park attempt abandons it instead.
ParkCount int `json:"parkCount,omitempty"`
// Abandoned is true once the periodic park sweep gave up on this
// catalog after too many parks -- terminal, no further automatic
// retries, though a fresh publish still reactivates it.
Abandoned bool `json:"abandoned,omitempty"`
// AbandonedAt is when Abandoned became true.
AbandonedAt time.Time `json:"abandonedAt,omitempty"`
// UpdatedAt is when this catalog's settled state was last written --
// i.e. its last successful sync or failure record, whichever is more
// recent.
UpdatedAt time.Time `json:"updatedAt,omitempty"`
// IndexLastPolledAt is when IndexURL was last actually fetched (any
// outcome, changed or not) -- the crawler has no per-index next-crawl
// timestamp of its own to report (PollIndexes polls every discovered
// index unconditionally every tick), so there is no exact
// "next scheduled crawl" to return; a caller wanting an estimate can
// add the deployment's own configured indexIntervalSeconds to this.
IndexLastPolledAt time.Time `json:"indexLastPolledAt,omitempty"`
}
CrawlStatus reports one catalog's last-known crawl/sync state, as currently persisted -- not a live check. Only fields the crawler actually populates today are included; see catalogcrawler's internal/store package for which crawler_catalog/crawler_queue/crawler_index columns are real versus unused schema inherited from an earlier prototype.
type Crawler ¶ added in v1.9.0
type Crawler interface {
// Start launches the background jobs; it returns immediately.
Start(ctx context.Context) error
// Stop signals the jobs and waits for the in-flight pass to drain.
Stop() error
// CrawlRegistry runs an immediate registry-backed crawl: it discovers the
// providers of the given networks (via the configured registry plugin's
// RegistryMetadataLookup) and crawls each -- the same registry-based
// input the scheduled pass uses, so a manual trigger and the background
// pass take one input model. Discovery always goes through the plugin
// instance configured at construction, which owns its own registry URL
// -- there is no per-call registry URL, so this cannot be pointed at a
// different registry than the one the deployment is configured with.
// Returns a run ID the caller can use to correlate the crawl's
// (asynchronous) log lines.
//
// ctx bounds only this call's synchronous validation, not the crawl
// itself: the crawl runs under the Crawler's own lifecycle (so it
// survives a request-scoped ctx returning, and is waited-for by Stop)
// rather than being canceled if ctx is.
CrawlRegistry(ctx context.Context, networkIDs []string) (string, error)
// Status reports the current crawl/sync state for every catalog owned
// by subscriberID (the authenticated caller -- see the catalogCrawlStatus
// handler), or just catalogID if it's non-empty. A catalogID not owned
// by subscriberID is indistinguishable from one that doesn't exist at
// all: both return an empty slice, not an error -- callers must not be
// able to probe for another subscriber's catalogIds.
Status(ctx context.Context, subscriberID, catalogID string) ([]CrawlStatus, error)
}
Crawler runs the decentralized-catalog crawl: discovering indexes, detecting which catalogs changed, and pushing each changed catalog's current content onward -- as background scheduled jobs, plus an on-demand trigger to run an immediate registry-backed crawl.
type CrawlerProvider ¶ added in v1.9.0
type CrawlerProvider interface {
New(ctx context.Context, registry RegistryLookup, metadataLookup RegistryMetadataLookup, config map[string]string) (Crawler, func() error, error)
}
CrawlerProvider initializes a new Crawler. It receives a RegistryLookup (used to resolve publisher signing keys -- catalog index entries and files self-sign, and the registry is the key distribution channel, exactly as signvalidator verifies transport signatures), a RegistryMetadataLookup (used to resolve each configured network's member providers via QueryByNetwork, for registry-backed discovery), and the plugin's config map.
RegistryLookup is REQUIRED for an enabled crawler: there is deliberately no per-deployment trusted-key configuration. RegistryMetadataLookup is REQUIRED whenever registry-backed discovery (the "networks" config) is used.
type Decrypter ¶
type Decrypter interface {
// Decrypt decrypts the given body using the provided privateKeyBase64 and publicKeyBase64.
Decrypt(ctx context.Context, encryptedData string, privateKeyBase64, publicKeyBase64 string) (string, error)
}
Decrypter defines the methods for decryption.
type DecrypterProvider ¶
type DecrypterProvider interface {
// New creates a new decrypter instance based on the provided config.
New(ctx context.Context, config map[string]string) (Decrypter, func() error, error)
}
DecrypterProvider initializes a new decrypter instance with the given config.
type Encrypter ¶
type Encrypter interface {
// Encrypt encrypts the given body using the provided privateKeyBase64 and publicKeyBase64.
Encrypt(ctx context.Context, data string, privateKeyBase64, publicKeyBase64 string) (string, error)
}
Encrypter defines the methods for encryption.
type EncrypterProvider ¶
type EncrypterProvider interface {
// New creates a new encrypter instance based on the provided config.
New(ctx context.Context, config map[string]string) (Encrypter, func() error, error)
}
EncrypterProvider initializes a new encrypter instance with the given config.
type FileRef ¶ added in v1.9.0
type FileRef struct {
FromVersion int
Version int
URL string
Size int64
Digest string
// Encoding names the artifact packaging ("" / "json" = plain JSON,
// "gzip" = gzipped JSON) -- carried through so a round trip via this
// type doesn't silently lose it even though a reader can still fall
// back to the URL's own suffix.
Encoding string
}
FileRef is a pointer to one published catalog file (a baseline or a change file): its own version, where it lives, its size and digest. Callers carry these forward across Publish calls -- Publish holds no storage-backed state of its own (see PriorCatalogState). File-level integrity now comes from the file's own embedded self-signature (file spec v2, "Catalog files and change files"), not a signature carried here -- trust for the index entry as a whole comes from CatalogPublishOutcome's catalog-entry-level signature instead. FromVersion is meaningful for a change file only (mirroring CatalogChangeFile's own fromVersion) -- zero for a baseline/latest FileRef. It must be carried explicitly rather than reconstructed from sequence order: once a catalog has been compacted, PriorCatalogState. ChangeFiles legitimately contains superseded (pre-compaction) entries alongside live (post-baseline) ones for the CON-TBD-32 grace period, so "whatever came immediately before it in the slice" is no longer a safe way to infer a change file's real FromVersion.
type KeyManager ¶
type KeyManager interface {
GenerateKeyset() (*model.Keyset, error)
InsertKeyset(ctx context.Context, keyID string, keyset *model.Keyset) error
Keyset(ctx context.Context, keyID string) (*model.Keyset, error)
LookupNPKeys(ctx context.Context, subscriberID, uniqueKeyID string) (signingPublicKey string, encrPublicKey string, err error)
DeleteKeyset(ctx context.Context, keyID string) error
}
KeyManager defines the interface for key management operations/methods.
type KeyManagerProvider ¶
type KeyManagerProvider interface {
New(context.Context, RegistryLookup, map[string]string) (KeyManager, func() error, error)
}
KeyManagerProvider initializes a new signer instance.
type ManifestLoader ¶ added in v1.6.0
type ManifestLoader interface {
GetByNetworkID(ctx context.Context, networkID string) (*model.ManifestDocument, error)
GetByMetadata(ctx context.Context, metadata model.ManifestMetadata) (*model.ManifestDocument, error)
// GetBySubscriberID fetches the node manifest for a specific subscriber.
// The subscriberID is the fully-qualified three-part DeDi reference (namespace/registry/recordName).
GetBySubscriberID(ctx context.Context, subscriberID string) (*model.ManifestDocument, error)
}
ManifestLoader fetches, verifies, caches, and returns manifest content.
type ManifestLoaderProvider ¶ added in v1.6.0
type ManifestLoaderProvider interface {
New(context.Context, Cache, RegistryMetadataLookup, map[string]string) (ManifestLoader, func() error, error)
}
ManifestLoaderProvider initializes a manifest loader instance with its dependencies. metaRegistry provides all DeDi path-based lookups the loader needs: LookupRegistry for network manifest discovery and LookupNode for subscriber manifest discovery.
type MasterDependency ¶ added in v1.9.0
MasterDependency is one MASTER catalog a REGULAR catalog's resources extend, per NFH-014 §10.3's dependencies.masters[]. IndexURL is an unauthenticated locator hint only (CON-TBD-31) -- a crawler still verifies whatever it fetches from it exactly as it would via ordinary discovery. Version is the MASTER's baseline.version last validated against -- the caller's responsibility to keep current as that changes, same as the rest of this struct (Publish only writes it through).
type MiddlewareProvider ¶
type OtelSetupMetricsProvider ¶ added in v1.3.0
type OtelSetupMetricsProvider interface {
// New initializes a new telemetry provider instance with the given configuration.
New(ctx context.Context, config map[string]string) (*telemetry.Provider, func() error, error)
}
OtelSetupMetricsProvider encapsulates initialization of OpenTelemetry metrics providers. Implementations wire exporters and return a Provider that the core application can manage.
type PayloadEntry ¶ added in v1.7.0
type PayloadEntry struct {
MessageID string
TransactionID string
NetworkID string
Action string
SubscriberID string
Role model.Role
RequestBody []byte // nil when StoreBody: false
Signature string // raw Authorization header; empty when StoreSignature: false
StoredAt time.Time
ExpiresAt time.Time
}
PayloadEntry is a single stored record for one BECKN message.
type PayloadStore ¶ added in v1.7.0
type PayloadStore interface {
// Store persists an entry built from the incoming request's StepContext.
Store(ctx *model.StepContext) error
// GetByTransactionID returns all entries for a transaction in StoredAt ascending order.
// Returns nil (not an error) if the transaction is unknown or expired.
GetByTransactionID(ctx context.Context, transactionID string) ([]PayloadEntry, error)
// GetByMessageID returns the entry for the given message ID scoped to an action.
// Returns nil (not an error) if not found or if the action does not match.
GetByMessageID(ctx context.Context, messageID, action string) (*PayloadEntry, error)
// Exists is an O(1) check for dedup / replay protection.
Exists(ctx context.Context, messageID string) (bool, error)
}
PayloadStore persists and retrieves payload entries indexed by message and transaction IDs.
type PayloadStoreProvider ¶ added in v1.7.0
type PayloadStoreProvider interface {
New(ctx context.Context, cache Cache, namespace string, cfg map[string]string) (PayloadStore, func() error, error)
}
PayloadStoreProvider is the plugin constructor interface.
type PolicyChecker ¶ added in v1.5.0
type PolicyChecker interface {
CheckPolicy(ctx *model.StepContext) error
}
PolicyChecker interface for policy checking on incoming messages.
type PolicyCheckerProvider ¶ added in v1.5.0
type PolicyCheckerProvider interface {
New(ctx context.Context, manifestLoader ManifestLoader, config map[string]string) (PolicyChecker, func(), error)
}
PolicyCheckerProvider interface for creating policy checkers.
type PriorCatalogState ¶ added in v1.9.0
type PriorCatalogState struct {
Catalog json.RawMessage
BaselineFile *FileRef
ChangeFiles []FileRef
EntryVersion int
CatalogType string
NetworkIds []string
SchemaTypes []string
IsActive bool
Dependencies []MasterDependency
CrawlHint string
// LatestPublished reports whether a "latest" full-CatalogFile pointer
// was previously published for this catalog (NFH-014 §Schema Changes,
// CON-TBD-38) -- independent of whether Config.PublishLatest is
// currently on, since retiring a catalog that had one MUST make one
// final write to that same stable URL populating CatalogFile.retiredAt,
// regardless of today's config. False (the zero value) for a catalog
// that never had one, in which case retiring it touches no "latest"
// file at all.
LatestPublished bool
}
PriorCatalogState is what a caller must supply, per catalogId, to get incremental (diffing) behavior instead of a fresh baseline. Publish is a pure function of (submissions, prior state) -> result; it never reads or writes any storage of its own. Catalog is the full, reconstructed content last published for this catalogId (baseline with every change file applied) -- diffing compares the new submission against this, not against the original baseline alone. A catalog's "current file version" is implicit: the last entry in ChangeFiles, or BaselineFile's version if ChangeFiles is empty -- matching the file spec (a file-lineage version lives per-file, not per-catalog-entry).
EntryVersion and the metadata fields below are the entry-level state (NFH-014 §Versioning's "catalog-entry level -- has anything changed"): distinct from the file-lineage version above, EntryVersion bumps on any change to the entry, content or metadata, so Publish needs the previously-published values to detect a metadata-only change (e.g. only NetworkIds edited, no new file) and still bump it correctly.
ChangeFiles doubles as the compaction grace-period mechanism (NFH-014 §10.1, CON-TBD-32): Publish itself holds no timer or storage, so whether -- and for how long -- to keep passing a compacted baseline's pre-compaction change files here (so they stay listed, not just hosted, for a DS mid-lineage) is entirely the caller's own policy to enforce by what it includes or drops from this slice on the next call.
type PublishError ¶ added in v1.9.0
type PublishError struct {
CatalogID string
Stage string // "validate" | "diff" | "sign"
Reason string
Fatal bool
}
PublishError is a non-fatal, per-catalog failure -- one bad submission must not fail the whole publish call, mirroring definition.CrawlError on the crawler side.
type PublishRequest ¶ added in v1.9.0
type PublishRequest struct {
Catalogs []CatalogSubmission
// PriorState supplies, per catalogId, what was last published for the
// catalogs actually submitted in Catalogs -- the only way Publish can
// produce a change file instead of a fresh baseline. A submitted
// catalogId absent from this map is always published as a new
// baseline, same as when ForceBaseline is set.
//
// The catalogpublisher plugin implementation ignores this field: since
// Publish now owns the full publish operation end to end (see Publish's
// doc comment), it loads its own prior state from its configured
// CatalogBlobStore rather than trusting a caller-supplied snapshot. The
// field stays on this struct as a caller-supplied *override* path for a
// future/alternate CatalogPublisher implementation that might want one
// (e.g. a test double, or a caller that legitimately owns storage
// itself) -- deciding whether to formally repurpose or remove it is a
// separate, later, better-scoped change.
PriorState map[string]PriorCatalogState
// Retire marks these catalogIds RETIRED this call: a tombstone entry
// (retiredAt, no isActive/baseline/changes) replaces whatever was
// there, per NFH-014 §10.4's "a retired catalog stays as a tombstone"
// rule. A retired catalogId's prior CatalogType/NetworkIds/SchemaTypes
// and EntryVersion still come from PriorState[id] -- a tombstone keeps
// those (NFH-014 Appendix A, Example 4's third entry), it only drops
// isActive/baseline/changes. A catalogId present in both Retire and
// Catalogs is published normally; Retire is ignored for it.
//
// This stays a batch-wide list here, kept distinct from
// CatalogSubmission, rather than becoming a third per-submission bool
// alongside ForceBaseline: catalog-core's own
// Submission.Directives.Retire needs a Submission to attach to, so the
// catalogpublisher plugin implementation synthesizes one (empty
// Catalog, Directives.Retire=true) per id here at Publish time --
// retiring a catalog you're not otherwise submitting content for
// should not require also inventing a CatalogSubmission for it at this
// layer.
Retire []string
}
PublishRequest is the input to CatalogPublisher.Publish.
type PublishResult ¶ added in v1.9.0
type PublishResult struct {
PublishedAt time.Time
// NodeID and NextUpdate are the index document's own top-level
// fields (NFH-014's "nodeId"/"next_update") -- domain and freshness
// window, resolved here from KeyManager's Keyset and Config.NextUpdateIn
// respectively, so a caller assembling the index doesn't have to
// duplicate that resolution itself.
NodeID string
NextUpdate *time.Time
Catalogs []CatalogPublishOutcome
Errors []PublishError
// Retirements carries each retired catalogId's signed tombstone entry
// -- separate from Catalogs, which reports outcomes for submitted
// catalogs only; retirement is a different kind of event with no
// Version/EntryVersion/Mode of its own to report there.
Retirements []RetirementOutcome
// RetiredLatest carries the final, self-signed CatalogFile write
// CON-TBD-38 requires for each retired catalogId whose PriorState had
// LatestPublished set: the same stable "latest" URL as before, now
// carrying CatalogFile.retiredAt, so a consumer that only ever fetches
// "latest" directly (never revisiting the index) can still learn the
// catalog is gone.
RetiredLatest []RetiredCatalogFile
// Warnings carries non-fatal operational notices produced alongside an
// otherwise-successful Publish call -- e.g. the registry catalog-index-
// link check reporting that this node's DeDi record doesn't yet list
// its catalog index. Distinct from Errors: an entry here is never a
// per-catalog business failure, just something an operator should look
// at.
Warnings []string
}
PublishResult is the output of a Publish call: per-catalog outcomes and errors, plus the two fields (NodeID/NextUpdate) a caller needs to stamp on the index document it assembles from those outcomes -- Publish itself never assembles or persists a catalog index as a whole (that is a storage layer's job, e.g. pkg/catalog/store.Store.Publish, which merges each outcome's SignedEntry into the index it already holds). There is no DeDi manifest here: the index's location is declared directly in the publisher's own DeDi registry record (meta.catalog_index_url, see IndexURL and core/module/handler/catalogPublishHandler.go's checkRegistryLinksCatalogIndex), not via a separate manifest document.
type Publisher ¶
type Publisher interface {
// Publish sends a message (as a byte slice) using the underlying messaging system.
Publish(context.Context, string, []byte) error
}
Publisher defines the general publisher interface for messaging plugins.
type PublisherProvider ¶
type PublisherProvider interface {
// New initializes a new publisher instance with the given configuration.
New(ctx context.Context, config map[string]string) (Publisher, func() error, error)
}
PublisherProvider is the interface for creating new Publisher instances.
type RegistryLookup ¶
type RegistryLookup interface {
// Lookup finds a registry entry by subscriberID and keyID (from the Authorization header)
// and returns the subscriber's public keys for incoming message signature validation.
// Input: req.SubscriberID (Beckn subscriber_id), req.KeyID (Beckn key_id).
Lookup(ctx context.Context, req *model.Subscription) ([]model.Subscription, error)
}
RegistryLookup resolves Beckn subscriber identities using generic Beckn protocol parameters. Inputs are subscriber-ID and key-ID as carried in the Beckn Authorization header.
type RegistryLookupProvider ¶
type RegistryLookupProvider interface {
New(context.Context, Cache, map[string]string) (RegistryLookup, func() error, error)
}
RegistryLookupProvider initializes a new registry lookup instance.
type RegistryMetadataLookup ¶ added in v1.6.0
type RegistryMetadataLookup interface {
// LookupRegistry fetches registry-level metadata for a DeDi network registry.
// Input: namespaceIdentifier (DeDi namespace, e.g. "nfh.global"),
// registryName (DeDi registry name, e.g. "retail.network.production").
// Returns registry metadata including manifest URLs used by ManifestLoader.
LookupRegistry(ctx context.Context, namespaceIdentifier, registryName string) (*model.RegistryMetadata, error)
// LookupNode fetches the full subscriber record for a DeDi node by its NodeID.
// Input: nodeID must be a fully-qualified three-part DeDi path in
// namespace/registry/recordName format (e.g. "nfh.global/subscribers.beckn.one/bpp.energy.com").
// Returns a SubscriberRecord with subscriber identity/endpoint data and any node manifest
// metadata from the same DeDi response. Meta is empty (not an error) when the participant
// has not yet published a node manifest.
// The full SubscriberRecord is available for any plugin to consume — manifest discovery
// (ManifestLoader) is the first use case but not the only one.
LookupNode(ctx context.Context, nodeID string) (*model.SubscriberRecord, error)
// QueryByNetwork fetches all subscriber records belonging to a DeDi network registry.
// Input: networkID in namespace/registryName DeDi path form (e.g. "beckn.one/testnet").
// Only records with state=="live" are returned. Meta/MetaArrays follow the same shape
// as LookupNode (e.g. MetaArrays["catalog_index_urls"]). AllowedNetworkIDs is not
// applied, same as LookupNode -- this is a discovery read, not a trust decision.
QueryByNetwork(ctx context.Context, networkID string) ([]model.SubscriberRecord, error)
}
RegistryMetadataLookup resolves DeDi registry and node records using DeDi-native path parameters. All inputs use the DeDi namespace/registry(/recordName) path convention — these are not generic Beckn params and are not interchangeable with the subscriberID/keyID used by RegistryLookup.
type ResponseStep ¶ added in v1.7.0
type ResponseStep interface {
RunOnResponse(ctx *model.StepContext, rctx *model.ResponseStepContext) error
}
ResponseStep is executed after all inbound Steps succeed, before the synchronous ACK is written back to the caller.
rctx is nil on the publisher path (ONIX writes the ACK itself); on the URL-routing path rctx carries the pre-read upstream response body, headers, and status code. Header is a shared reference — mutations (e.g. writing a Signature header) are forwarded by ReverseProxy without explicit write-back.
type RetiredCatalogFile ¶ added in v1.9.0
type RetiredCatalogFile struct {
CatalogID string
Content json.RawMessage
ServedContent []byte
Compressed bool
}
RetiredCatalogFile is one retired catalog's final "latest" write (see PublishResult.RetiredLatest). Content is the canonical (uncompressed) signed bytes; ServedContent is what a caller should actually write to the "latest" URL -- identical to Content unless Compressed is true, same convention as CatalogPublishOutcome's Content/ServedContent pair.
type RetirementOutcome ¶ added in v1.9.0
type RetirementOutcome struct {
CatalogID string
SignedEntry json.RawMessage
}
RetirementOutcome is one retired catalog's signed tombstone entry -- ready to hand a catalog storage layer to merge into the index, same as CatalogPublishOutcome.SignedEntry for a submitted catalog.
type Router ¶
type Router interface {
// Route determines the routing destination based on the request context.
Route(ctx context.Context, url *url.URL, body []byte) (*model.Route, error)
}
Router defines the interface for routing requests.
type RouterProvider ¶
type RouterProvider interface {
New(ctx context.Context, config map[string]string) (Router, func() error, error)
}
RouterProvider initializes the a new Router instance with the given config.
type SchemaValidator ¶
type SchemaValidator interface {
Validate(ctx context.Context, url *url.URL, payload []byte) error
}
SchemaValidator interface for schema validation.
type SchemaValidatorProvider ¶
type SchemaValidatorProvider interface {
New(ctx context.Context, config map[string]string) (SchemaValidator, func() error, error)
}
SchemaValidatorProvider interface for creating validators.
type SchemaVersionMediator ¶ added in v1.8.0
type SchemaVersionMediator interface {
Mediate(ctx *model.StepContext) error
}
SchemaVersionMediator mediates schema version differences in a Beckn payload. It walks the payload, checks compatibility against the node manifest, fetches translation artifacts, dispatches to the appropriate translator, and enforces the configured data-loss policy.
Mediate is direction-agnostic: both the receiver handler (inbound) and the caller handler (outbound) invoke it via their respective StepContext.
type SchemaVersionMediatorProvider ¶ added in v1.8.0
type SchemaVersionMediatorProvider interface {
New(ctx context.Context, loader ManifestLoader, config map[string]string) (SchemaVersionMediator, func() error, error)
}
SchemaVersionMediatorProvider initializes a SchemaVersionMediator with its dependencies. loader is injected so the mediator can fetch the node manifest for the network at runtime without owning the fetch/cache lifecycle.
type SignValidator ¶
type SignValidator interface {
// Validate verifies the 3-line signing string for inbound requests.
// The request body is available as ctx.Body.
// checkIdentity controls whether the signer's subscriber ID (from keyId) is
// matched against the caller identity declared in the request body context.
// Pass true for subscriber Authorization headers, false for gateway headers.
Validate(ctx *model.StepContext, header string, publicKeyBase64 string, checkIdentity bool) error
// ValidateAck verifies a Beckn v2.0.0 AckSignature per NFH-004 §3.4.
// The four-line signing string is:
// (created): <ts>
// (expires): <ts>
// digest: BLAKE-512=<base64(blake2b512(body))>
// request-signature: <outboundAuthSignature>
// outboundAuthSignature is the raw Base64 signature value from the original
// outbound Authorization header's signature="..." attribute. If empty the
// fourth line is omitted (matches the ackSigner signing-string construction).
// body is passed explicitly because different call sites hash different bodies:
// solicited callback bodies differ from synchronous ACK response bodies.
// checkIdentity: true for solicited callbacks (step.go), false for ACK responses (responsestep.go).
ValidateAck(ctx *model.StepContext, body []byte, signatureHeader, outboundAuthSignature, publicKeyBase64 string, checkIdentity bool) error
}
SignValidator defines the method for verifying signatures.
type SignValidatorProvider ¶
type SignValidatorProvider interface {
// New creates a new Verifier instance based on the provided config.
New(ctx context.Context, config map[string]string) (SignValidator, func() error, error)
}
SignValidatorProvider initializes a new Verifier instance with the given config.
type Signer ¶
type Signer interface {
// Sign generates a signature for the given body and privateKeyBase64.
// The signature is created with the given timestamps: createdAt (signature creation time)
// and expiresAt (signature expiration time).
Sign(ctx context.Context, body []byte, privateKeyBase64 string, createdAt, expiresAt int64) (string, error)
// SignAck generates a signature for a synchronous Ack response using the
// NFH-004 §3.4 four-line signing string:
// (created): <ts>
// (expires): <ts>
// digest: BLAKE-512=<base64(blake2b512(ackBody))>
// request-signature: <requestSignature>
// requestSignature is the raw Base64 value from the inbound Authorization
// header's signature="..." attribute. If empty the fourth line is omitted.
SignAck(ctx context.Context, ackBody []byte, requestSignature, privateKeyBase64 string, createdAt, expiresAt int64) (string, error)
}
Signer defines the method for signing.
type SignerProvider ¶
type SignerProvider interface {
// New creates a new signer instance based on the provided config.
New(ctx context.Context, config map[string]string) (Signer, func() error, error)
}
SignerProvider initializes a new signer instance with the given config.
type Step ¶
type Step interface {
Run(ctx *model.StepContext) error
}
Step is executed on the inbound request as part of the processing pipeline.
type StepProvider ¶
type Translator ¶ added in v1.8.0
type Translator interface {
Translate(ctx context.Context, artifact []byte, payload []byte) ([]byte, error)
}
Translator executes a single translation pass over a payload fragment using a fetched translation artifact. Implementations are stateless — all state required for translation is carried in the artifact bytes themselves.
artifact is the raw bytes of the translation artifact fetched from schema.beckn.io (e.g. a JSONata expression). payload is the payload fragment to be translated. The translated fragment is returned as a new byte slice.
The mediator selects the Translator implementation by matching the artifact's Content-Type, allowing a single mediation pass to dispatch different fragments to different translators (e.g. JSONata for one schema delta, SHACL for another).
type TranslatorProvider ¶ added in v1.8.0
type TranslatorProvider interface {
New(ctx context.Context, config map[string]string) (Translator, func() error, error)
}
TranslatorProvider initializes a Translator instance with its configuration.
type TransportWrapper ¶ added in v1.3.0
type TransportWrapper interface {
// Wrap takes a base transport and returns a new transport that wraps it.
Wrap(base http.RoundTripper) http.RoundTripper
}
TransportWrapper is a plugin that wraps an http.RoundTripper, allowing modification of outbound requests (like adding auth).
type TransportWrapperProvider ¶ added in v1.3.0
type TransportWrapperProvider interface {
New(ctx context.Context, config map[string]any) (TransportWrapper, func(), error)
}
TransportWrapperProvider defines the factory for a TransportWrapper.
Source Files
¶
- cache.go
- catalogblobstore.go
- catalogcrawler.go
- catalogpublisher.go
- decrypter.go
- encrypter.go
- keymanager.go
- manifestloader.go
- metrics.go
- middleware.go
- payloadstore.go
- policyChecker.go
- publisher.go
- registry.go
- router.go
- schemaValidator.go
- schemaversionmediator.go
- signer.go
- signvalidator.go
- step.go
- translator.go
- transport.go