app

package
v0.47.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: AGPL-3.0 Imports: 12 Imported by: 0

Documentation

Overview

Package app holds the use cases of Arnakoff and the ports they consume. It depends on domain only (ADR 0002).

Index

Constants

View Source
const KeyCacheTTL = 30 * time.Second

KeyCacheTTL is how long a KeyCache keeps an active key: a key revoked in the store is still accepted for at most this long. Validated by the owner on 02/11/2026 (#198).

Variables

View Source
var (
	// ErrKeyNotFound means no key has this digest, or this identifier.
	ErrKeyNotFound = errors.New("API key not found")
	// ErrKeyRevoked means the key was revoked.
	ErrKeyRevoked = errors.New("API key revoked")
)

API key errors, checked with errors.Is. Any other error of a key store means it could not answer.

View Source
var (
	// ErrNotFound means the registry answered that it holds no such entry:
	// an answer, not a failure.
	ErrNotFound = errors.New("not found in the registry")
	// ErrNotCovered means the registry does not cover this input, such as a
	// domain extension without an RDAP server.
	ErrNotCovered = errors.New("not covered by the registry")
)

Registry errors, wrapped by the registry adapters and checked with errors.Is.

View Source
var (
	ErrUnknownSource   = errors.New("unknown source")
	ErrDuplicateSource = errors.New("duplicate source")
)

Sync errors, checked with errors.Is.

View Source
var ErrInterval = errors.New("sync interval must be positive")

ErrInterval means a plan has no positive interval.

View Source
var ErrNoSnapshot = errors.New("no stored snapshot")

ErrNoSnapshot is returned by SnapshotStore.Load when it holds no snapshot of the source.

View Source
var ErrUndescribedSource = errors.New("synced source without info")

ErrUndescribedSource is returned by NewSourceList when a synced source has no SourceInfo.

View Source
var ErrUnknownLabel = errors.New("unknown corpus label")

ErrUnknownLabel means a case is labelled neither scam nor legit.

Functions

This section is empty.

Types

type Alert added in v0.19.0

type Alert struct {
	Source domain.Source
	At     time.Time
	Err    error
}

Alert is a sync or reload of one source that failed or was rejected.

type Alerter added in v0.19.0

type Alerter interface {
	Alert(ctx context.Context, a Alert)
}

Alerter tells the operator that a source could not be synced or reloaded. It reports its own failures: the caller has nothing to do about them.

type Authenticator added in v0.39.0

type Authenticator struct {
	// contains filtered or unexported fields
}

Authenticator tells whether an API key may call the API.

func NewAuthenticator added in v0.39.0

func NewAuthenticator(keys KeyFinder, secret []byte, env domain.KeyEnvironment) *Authenticator

NewAuthenticator returns an Authenticator accepting the active keys of env found in keys, digested with secret.

func (*Authenticator) Authenticate added in v0.39.0

func (a *Authenticator) Authenticate(ctx context.Context, raw string) (domain.KeyRecord, error)

Authenticate returns the record of the key raw. It fails with domain.ErrInvalidAPIKey for a malformed key, ErrKeyNotFound for a key never issued or issued for another environment, ErrKeyRevoked for a revoked key, and any other error when the store could not answer.

type Blacklist added in v0.25.0

type Blacklist struct {
	Rule   signal.Blacklist
	Reader BlacklistReader
}

Blacklist is a blacklist a Check reads, and the rule its listings follow.

type BlacklistReader added in v0.25.0

type BlacklistReader interface {
	// Blacklist returns the blacklist in service and when it was fetched,
	// or ErrNoSnapshot when none is. The blacklist is shared: treat it as
	// read-only.
	Blacklist() (*domain.Blacklist, time.Time, error)
}

BlacklistReader reads a blacklist held in memory.

type Case added in v0.23.0

type Case struct {
	Input domain.Input
	Label Label
}

Case is one labelled input of the evaluation corpus.

type Check added in v0.23.0

type Check struct {
	// contains filtered or unexported fields
}

Check reads the blacklists, the white lists, the numbering plan and the input itself, and consults the registries, all at once, about an input, and reports the signals they give and the state of each. It is safe for concurrent use when the registries are.

func NewCheck added in v0.23.0

func NewCheck(r Registries, clock Clock, opts ...CheckOption) *Check

NewCheck returns a Check of the registries, which dates its findings with clock.

func (*Check) Check added in v0.23.0

func (c *Check) Check(ctx context.Context, in domain.Input) domain.Report

Check returns the report of in, dated when it starts. A list without a snapshot in service, or a registry that fails or has not answered when the deadline comes, is reported unavailable; the others are still reported.

func (*Check) CheckAll added in v0.36.0

func (c *Check) CheckAll(ctx context.Context, ins []domain.Input) []domain.Report

CheckAll returns the report of each input, in the order of ins, as Check returns it. It checks several inputs at once, up to the batch limit, and the timeout bounds the whole batch rather than each check: an input whose turn comes at the deadline is reported with its registries unavailable.

type CheckOption added in v0.24.0

type CheckOption func(*Check)

CheckOption configures a Check.

func WithBatchLimit added in v0.36.0

func WithBatchLimit(n int) CheckOption

WithBatchLimit makes CheckAll check at most n inputs at once; n below 1 means one.

func WithTimeout added in v0.24.0

func WithTimeout(d time.Duration) CheckOption

WithTimeout bounds every check to d, or to the deadline of its context when that comes first.

type Checker added in v0.23.0

type Checker interface {
	Check(ctx context.Context, in domain.Input) domain.Report
}

Checker returns the report of an input; *Check is one.

type Clock

type Clock interface {
	Now() time.Time
}

Clock tells the current time; tests inject a fixed one.

type CompanyRegistry added in v0.23.0

type CompanyRegistry interface {
	// Source names the registry in reports.
	Source() domain.Source
	// Lookup returns the company n identifies.
	Lookup(ctx context.Context, n domain.SIREN) (domain.Company, error)
}

CompanyRegistry reads what the company register publishes about a company.

type DomainRegistry added in v0.23.0

type DomainRegistry interface {
	// Source names the registry in reports.
	Source() domain.Source
	// Lookup returns the registration of d's registrable domain.
	Lookup(ctx context.Context, d domain.Domain) (domain.Registration, error)
}

DomainRegistry reads what a domain registry publishes about a domain.

type FalseAlert added in v0.23.0

type FalseAlert struct {
	// Input is the normalised input.
	Input string
	// Level is the most severe level it got.
	Level domain.Level
}

FalseAlert is a legit case that got an attention or an alerte.

type Jitter added in v0.19.0

type Jitter func(maxJitter time.Duration) time.Duration

Jitter returns a random duration from 0 up to, but not including, max.

type KeyAdmin added in v0.39.0

type KeyAdmin struct {
	// contains filtered or unexported fields
}

KeyAdmin issues, lists and revokes API keys.

func NewKeyAdmin added in v0.39.0

func NewKeyAdmin(keys KeyIssuer, secret []byte, clock Clock, random io.Reader) *KeyAdmin

NewKeyAdmin returns a KeyAdmin storing in keys the digests under secret of keys drawn from random, such as crypto/rand.Reader.

func (*KeyAdmin) Issue added in v0.39.0

Issue draws a key of env for the holder label, stores its record and returns both: the key is shown once and never kept.

func (*KeyAdmin) List added in v0.39.0

func (a *KeyAdmin) List(ctx context.Context) ([]domain.KeyRecord, error)

List returns the record of every issued key, oldest first.

func (*KeyAdmin) Revoke added in v0.39.0

func (a *KeyAdmin) Revoke(ctx context.Context, id int64) error

Revoke revokes the key id now, or returns ErrKeyNotFound.

type KeyCache added in v0.43.0

type KeyCache struct {
	// contains filtered or unexported fields
}

KeyCache is a KeyFinder keeping in memory, for a fixed time, the active keys its store finds, so that a valid key does not cost a store lookup on each request. It keeps nothing else: an unknown or revoked key and a store failure reach the store each time, so a revocation is seen as soon as the cached record expires and an outage is never hidden. Its size is bounded by the number of active keys issued, not by what clients send.

func NewKeyCache added in v0.43.0

func NewKeyCache(keys KeyFinder, clock Clock, ttl time.Duration) *KeyCache

NewKeyCache returns a KeyCache over keys keeping each active record for ttl, as told by clock.

func (*KeyCache) FindKey added in v0.43.0

func (c *KeyCache) FindKey(ctx context.Context, d domain.KeyDigest) (domain.KeyRecord, error)

FindKey returns the record of the key of digest d, from memory while it is fresh, or else from the store.

type KeyFinder added in v0.39.0

type KeyFinder interface {
	// FindKey returns the record of the key of digest d, or ErrKeyNotFound.
	FindKey(ctx context.Context, d domain.KeyDigest) (domain.KeyRecord, error)
}

KeyFinder finds an issued API key by its digest.

type KeyIssuer added in v0.39.0

type KeyIssuer interface {
	// CreateKey stores r and returns it with its identifier set.
	CreateKey(ctx context.Context, r domain.KeyRecord) (domain.KeyRecord, error)
	// ListKeys returns every record, oldest first.
	ListKeys(ctx context.Context) ([]domain.KeyRecord, error)
	// RevokeKey marks the key id revoked at at, or returns ErrKeyNotFound.
	// A key already revoked keeps its first revocation time.
	RevokeKey(ctx context.Context, id int64, at time.Time) error
}

KeyIssuer keeps the records of issued API keys.

type Label added in v0.23.0

type Label string

Label tells what a corpus case is known to be.

const (
	// LabelScam is an input an authority blacklisted.
	LabelScam Label = "scam"
	// LabelLegit is an input of a verified legitimate entity.
	LabelLegit Label = "legit"
)

Corpus labels.

type ListedSource added in v0.37.0

type ListedSource struct {
	Info domain.SourceInfo
	// Synced tells whether Sync downloads the source; false for a registry
	// queried on demand or a reference list built into the binary.
	Synced bool
	// Sync is the state of the data in service of a synced source.
	Sync SourceSync
}

ListedSource is one source as GET /v1/sources shows it.

type NoticeRegistry added in v0.23.0

type NoticeRegistry interface {
	// Source names the registry in reports.
	Source() domain.Source
	// Notices returns the notices about the company n identifies.
	Notices(ctx context.Context, n domain.SIREN) ([]domain.Notice, error)
}

NoticeRegistry reads the legal notices published about a company.

type Numbering added in v0.28.0

type Numbering struct {
	Reader NumberingReader
}

Numbering is the numbering plan a Check reads.

type NumberingReader added in v0.28.0

type NumberingReader interface {
	// Numbering returns the plan in service and when it was fetched, or
	// ErrNoSnapshot when none is. The plan is shared: treat it as
	// read-only.
	Numbering() (*domain.NumberingPlan, time.Time, error)
}

NumberingReader reads the numbering plan held in memory.

type Plan added in v0.19.0

type Plan struct {
	Source domain.Source
	Every  time.Duration
}

Plan says how often a source is synced.

type Registries added in v0.23.0

type Registries struct {
	Blacklists []Blacklist
	Whitelists []Whitelist
	Numbering  []Numbering
	Officials  signal.Officials
	Domains    DomainRegistry
	Companies  CompanyRegistry
	Notices    NoticeRegistry
}

Registries are the sources a Check consults: the blacklists, the white lists, the numbering plan, then the registries. A nil registry is left out of the reports. Officials, the reference list of official domains, is read with the input and never reported as a source.

func (Registries) Without added in v0.23.0

func (r Registries) Without(leave func(domain.Source) bool) Registries

Without returns r less the registries leave reports true for.

type Reloader added in v0.19.0

type Reloader struct {
	// contains filtered or unexported fields
}

Reloader restores, at an interval, the snapshots the sync process stored, so that the API process serves them without a restart. It alerts when a source starts failing to reload, not again until it has recovered.

func NewReloader added in v0.19.0

func NewReloader(s *Sync, sleeper Sleeper, alerter Alerter, every time.Duration) *Reloader

NewReloader returns a Reloader of the sources of s, checking every interval.

func (*Reloader) Run added in v0.19.0

func (r *Reloader) Run(ctx context.Context)

Run reloads every source at each interval until ctx is done. Restore the snapshots once before, so the first ones are in service at once.

type Scheduler added in v0.19.0

type Scheduler struct {
	// contains filtered or unexported fields
}

Scheduler syncs each planned source at its interval, plus a jitter, and alerts on every sync that fails or is rejected.

func NewScheduler added in v0.19.0

func NewScheduler(s *Sync, sleeper Sleeper, alerter Alerter, jitter Jitter, plans ...Plan) (*Scheduler, error)

NewScheduler returns a Scheduler of plans, or ErrUnknownSource, ErrDuplicateSource or ErrInterval for a plan s cannot follow.

func (*Scheduler) Once added in v0.19.0

func (sc *Scheduler) Once(ctx context.Context, sources ...domain.Source) error

Once syncs each of sources once, or every planned source when none is given, alerts on each failure and joins the errors. It returns ErrUnknownSource without syncing anything when a source is not planned.

func (*Scheduler) Run added in v0.19.0

func (sc *Scheduler) Run(ctx context.Context)

Run syncs every planned source until ctx is done. A source whose snapshot in service is younger than its interval waits until it is due: restore the stored snapshots first so that a restart does not download every source again.

type Score added in v0.23.0

type Score struct {
	// Scams is the number of scam cases.
	Scams int
	// ScamsAlerted counts the scams with at least one alerte.
	ScamsAlerted int
	// ScamsFlagged counts the scams with at least one attention or alerte.
	ScamsFlagged int
	// Legit is the number of legit cases.
	Legit int
	// LegitAlerted counts the legit cases with at least one alerte; it must
	// stay 0.
	LegitAlerted int
	// FalseAlerts lists the legit cases with at least one attention or
	// alerte, in corpus order.
	FalseAlerts []FalseAlert
	// Unavailable counts the cases a source could not be consulted for:
	// their score proves less.
	Unavailable int
}

Score counts the corpus cases each signal level reached.

func Evaluate added in v0.23.0

func Evaluate(ctx context.Context, c Checker, cases []Case) (Score, error)

Evaluate checks every case in turn and scores the reports.

type Sleeper added in v0.19.0

type Sleeper interface {
	// Sleep returns after d, or with the error of ctx once it is done.
	Sleep(ctx context.Context, d time.Duration) error
}

Sleeper pauses a scheduling loop; tests inject one that does not wait.

type SnapshotStore

type SnapshotStore interface {
	// Save durably replaces the stored snapshot of s.Source.
	Save(ctx context.Context, s domain.Snapshot) error
	// Load returns the stored snapshot of source, or ErrNoSnapshot.
	Load(ctx context.Context, source domain.Source) (domain.Snapshot, error)
}

SnapshotStore keeps the last valid snapshot of each source across restarts and between the sync and API processes.

func DryRun added in v0.19.0

func DryRun(st SnapshotStore) SnapshotStore

DryRun returns a store that reads st and keeps nothing: a Sync on it, once restored, validates new snapshots against the stored ones without replacing them.

type SourceFetcher

type SourceFetcher interface {
	// Spec declares the source and what a sane file of it holds.
	Spec() domain.SnapshotSpec
	// Fetch downloads the source and parses it into a snapshot. Sync sets
	// its FetchedAt.
	Fetch(ctx context.Context) (domain.Snapshot, error)
}

SourceFetcher downloads and parses one upstream source.

type SourceList added in v0.37.0

type SourceList struct {
	// contains filtered or unexported fields
}

SourceList lists the sources a check consults: the synced ones, with the state of their data in service, then the others. It is safe for concurrent use.

func NewSourceList added in v0.37.0

func NewSourceList(s *Sync, synced []domain.SourceInfo, others ...domain.SourceInfo) (*SourceList, error)

NewSourceList returns the list of the sources of s, described by synced, one info each, then of others. It returns ErrUnknownSource for an info of no source of s, ErrDuplicateSource for a source described twice and ErrUndescribedSource for a source of s without info.

func (*SourceList) Sources added in v0.37.0

func (l *SourceList) Sources() []ListedSource

Sources returns every source, the synced ones first, in the order of the sync.

type SourceSync

type SourceSync struct {
	Source domain.Source
	// LastSuccess is when the snapshot in service was fetched, zero when
	// none is.
	LastSuccess time.Time
	// LastFailure is when the last failed or rejected sync ran, zero when
	// none did. It is kept after a later success.
	LastFailure time.Time
	// LastError is why that sync failed.
	LastError error
	// Rows is the number of rows of the snapshot in service.
	Rows int
	// Newest is the date of the most recent entry in service, zero when the
	// source dates none.
	Newest time.Time
}

SourceSync is the sync state of one source, as /v1/sources shows it.

type Sync

type Sync struct {
	// contains filtered or unexported fields
}

Sync downloads each source, validates the new snapshot against the connector's spec and the last valid one, stores it, then swaps it in for the readers. A rejected snapshot leaves the last valid one in service. It is safe for concurrent use.

func NewSync

func NewSync(store SnapshotStore, clock Clock, fetchers ...SourceFetcher) (*Sync, error)

NewSync returns a Sync of the sources the fetchers declare, or ErrDuplicateSource when two declare the same one.

func (*Sync) Ready added in v0.31.0

func (s *Sync) Ready() bool

Ready reports whether every source has a snapshot in service: until then, a check would miss a whole list.

func (*Sync) Restore

func (s *Sync) Restore(ctx context.Context) error

Restore puts in service the stored snapshot of every source when it is valid and newer than the one in service. It runs at startup, and in the API process to pick up what the sync process stored. A source with no stored snapshot is skipped; errors of the others are joined.

func (*Sync) Run

func (s *Sync) Run(ctx context.Context) error

Run syncs every source in turn and joins their errors: one source failing does not stop the others.

func (*Sync) RunSource

func (s *Sync) RunSource(ctx context.Context, source domain.Source) error

RunSource syncs one source, or returns ErrUnknownSource.

func (*Sync) Snapshot

func (s *Sync) Snapshot(source domain.Source) (domain.Snapshot, bool)

Snapshot returns the snapshot in service for source, if any. It is shared with every reader: treat it as read-only.

func (*Sync) States

func (s *Sync) States() []SourceSync

States returns the sync state of every source, in declaration order.

type Whitelist added in v0.26.0

type Whitelist struct {
	Rule   signal.Whitelist
	Reader WhitelistReader
}

Whitelist is a white list a Check reads, and the rule its authorisations follow.

type WhitelistReader added in v0.26.0

type WhitelistReader interface {
	// Whitelist returns the white list in service and when it was fetched,
	// or ErrNoSnapshot when none is. The white list is shared: treat it as
	// read-only.
	Whitelist() (*domain.Whitelist, time.Time, error)
}

WhitelistReader reads a white list held in memory.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL