Documentation
¶
Overview ¶
Package writer is the audit writer as a library: the thing that takes records, splits them into the copies their profiles keep, pseudonymises, and puts them into the locked archive.
This is what the audit-writer binary runs: that binary is built on this package, and the promises are the writer's rather than the binary's. Nothing is acknowledged before it is in the archive, a record the writer cannot take is dead-lettered and never dropped, and the writer keeps an account of itself in the archive it writes.
It is exported because the binary needs it and a test may. It is not a way to deploy: a writer inside an application puts the archive's credentials in the application's pods and makes every fix an application release, which docs/decisions/0011-one-installation-per-service-or-product.md rules out.
w, err := writer.Open(ctx, writer.Config{
Archive: archive, // an s3store.Store on the Object-Locked bucket
Profiles: profiles, // preset.ParseDeployment(doc) then Compose
Keys: provider, // keys.NewTransit or keys.NewLocal
})
defer w.Close(ctx)
Index ¶
- type Config
- type Dedupe
- type Evidence
- type Writer
- func (w *Writer) Close(ctx context.Context) error
- func (w *Writer) Guarantees() sink.Durability
- func (w *Writer) Handler(a auth.Authenticator) (string, http.Handler)
- func (w *Writer) RefreshHolds(ctx context.Context) error
- func (w *Writer) Write(ctx context.Context, req *sink.Request) (*sink.Result, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// Archive is where the copies go: the Object-Locked bucket.
Archive store.Store
// Profiles are the deployment's profiles, composed from the presets —
// preset.ParseDeployment and Compose read the same document the chart
// renders.
Profiles map[string]*preset.Profile
// Keys pseudonymise identifiers. With a provider that can also seal
// (keys.Transit, keys.Local), the identity behind each pseudonym is kept
// sealed under the same key, for resolve; see ForgetIdentities.
//
// Optional, and nil is the default a deployment should have to argue
// itself out of rather than into: most trails are of an organisation's
// own staff, who are kept in clear because that is what accountability
// is for. Open refuses nil only when a composed profile would actually
// pseudonymise, which is what GuardKeys decides.
Keys keys.Provider
// Catalogues are the application's own. The common catalogue, which
// describes the writer's own actions, is always registered.
Catalogues []*catalogue.Catalogue
// Database, when given, is the deduplication table shared by every
// replica, and where catalogues registered through the registry service
// are read from. The writer's role needs those tables and none of the
// index: observe indexes, by following the bucket. Without a database
// the writer deduplicates in process, and so may run as one instance
// only.
Database *pgxpool.Pool
// Dedupe, when given, is the deduplication store, in place of the one
// Database provides: for a deployment that has no database, such as the
// writer on a function platform, where it is a DynamoDB table
// (dedupe/dynamodbdedupe). It is shared by every replica, so it lifts the
// one-instance limit as Database does. Give it or Database, not both.
Dedupe Dedupe
// Replicas is how many writers share one stream of records. Above one it
// needs Database, and keys every replica sees the same way.
Replicas int
// Self is this writer's own name as an observer, stamped on records that
// reach it in process — an embedding application's emitter is the
// application itself, so this names it. A record that arrives over HTTP
// carries the caller the auth middleware verified instead.
Self string
// ForgetIdentities keeps no sealed identities, which makes resolve
// impossible for everything this writer writes.
ForgetIdentities bool
// Instance names this writer in its own records. It is not in any object
// key: the key's ULID is what keeps two writers apart.
// Default: the host name and process id.
Instance string
// RollInterval is how long an object stays open within a batch. Default
// 5 minutes; every batch is flushed before it is acknowledged regardless.
RollInterval time.Duration
// Version names this build in the writer's own records.
Version string
// Evidence says, in the writer's start-up record, which configuration it
// ran under: digests of the files it read. Empty says nothing.
Evidence Evidence
// FromStream says this writer consumes a stream its own installation's
// receivers publish to, so a record arriving already stamped keeps that
// stamp. A consumer reading messages has no caller to verify, and
// re-stamping would replace the identity the receiver checked with
// nothing. It has no effect on records reaching the sink's own port.
FromStream bool
// Logger, default slog.Default().
Logger *slog.Logger
// Meter is where the writer's counters go, default the global provider.
Meter metric.MeterProvider
}
Config is what a writer needs. Archive and Profiles are required; everything else has a default that is safe for one instance.
type Dedupe ¶ added in v0.7.0
Dedupe is the writer's record of what it has written: Seen asks and marks nothing, Mark is called once the copies are durable, Purge forgets. See dedupe/dynamodbdedupe and index/postgres for the shared ones.
type Evidence ¶ added in v0.11.0
Evidence is what a writer reports of its configuration in its start-up record (audit.writer.started).
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer is an open writer. It is a sink.Sink: hand it to an emitter, or serve it over HTTP with Handler.
func Open ¶
Open checks the configuration, reads the legal holds, records each profile's composition, and returns a writer ready to take records.
It refuses rather than degrades: a writer that could not read the holds, or could not record a change of profile, or whose replicas would pseudonymise the same person differently, does not start.
func (*Writer) Close ¶
Close records that the writer stopped, writes whatever is still gathered, and stops watching the legal holds. Nothing is taken after it.
func (*Writer) Guarantees ¶ added in v0.6.0
func (w *Writer) Guarantees() sink.Durability
Guarantees implements sink.Guarantor.
func (*Writer) Handler ¶
Handler serves the writer as the sink's Connect service, for emitters in other processes. With an authenticator, every caller must present a token it verifies, and the verified subject is stamped as the record's observer; nil accepts anybody, under no name.
func (*Writer) RefreshHolds ¶ added in v0.7.0
RefreshHolds reads the legal holds now. The writer refreshes them every minute in the background, which a process that is frozen between invocations does not get: its clock keeps running while it is stopped, and the first write after a thaw could act on a list that is older than it looks. A function calls this at the start of each invocation. An error leaves the last answer in force, the way a failed background refresh does.