Documentation
¶
Overview ¶
Package config reads the configuration of every command from environment variables into one typed Config, validated as a whole at startup so a command stops before doing anything with a value it cannot use (ADR 0002).
The variables, their defaults and an example are in .env.example. A secret, such as the log key, is held as a Secret: it never appears in a log line, an error message or a formatted Config.
Commands write only under Config.DataDir, the data volume, so the rest of the file system can be mounted read-only.
Index ¶
- Constants
- Variables
- type Backup
- type Config
- func (c Config) RequireBackup() error
- func (c Config) RequireDatabase() error
- func (c Config) RequireErase() error
- func (c Config) RequireKeyStore() error
- func (c Config) RequireRestore() error
- func (c Config) RequireRotate() error
- func (c Config) RequireTrustedProxies() error
- func (c Config) SnapshotDir() string
- type Demo
- type Erase
- type GitHubApp
- type KeyRequest
- type Restore
- type Rotate
- type RotateApps
- type RotateSMTP
- type Secret
- type SecretIDs
- type SnapshotBucket
Constants ¶
const ( EnvBackupBucket = "ARNAKOFF_BACKUP_BUCKET" EnvBackupBucketAccessKey = "ARNAKOFF_BACKUP_BUCKET_ACCESS_KEY" EnvBackupBucketSecretKey = "ARNAKOFF_BACKUP_BUCKET_SECRET_KEY" //nolint:gosec // G101: the name of a variable, not its value. EnvBackupRecipient = "ARNAKOFF_BACKUP_RECIPIENT" )
Environment variables of the backup of the key store (#75), read by Load.
const ( EnvAddr = "ARNAKOFF_ADDR" EnvLogLevel = "ARNAKOFF_LOG_LEVEL" EnvLogKey = "ARNAKOFF_LOG_KEY" EnvDataDir = "ARNAKOFF_DATA_DIR" EnvSourceACPR = "ARNAKOFF_SOURCE_ACPR" EnvAlertWebhook = "ARNAKOFF_ALERT_WEBHOOK" EnvProblemBaseURL = "ARNAKOFF_PROBLEM_BASE_URL" EnvBatchMaxItems = "ARNAKOFF_BATCH_MAX_ITEMS" EnvInstances = "ARNAKOFF_INSTANCES" EnvDatabaseURL = "ARNAKOFF_DATABASE_URL" EnvDatabaseCA = "ARNAKOFF_DATABASE_CA" EnvAPIKeySecret = "ARNAKOFF_API_KEY_SECRET" //nolint:gosec // G101: the name of a variable, not its value. EnvAPIKeyEnv = "ARNAKOFF_API_KEY_ENV" //nolint:gosec // G101: the name of a variable, not its value. EnvRateLimitKey = "ARNAKOFF_RATE_LIMIT_KEY" EnvRateLimitIP = "ARNAKOFF_RATE_LIMIT_IP" EnvRateLimitAuth = "ARNAKOFF_RATE_LIMIT_AUTH" EnvTrustedProxies = "ARNAKOFF_TRUSTED_PROXIES" EnvClientIPHeader = "ARNAKOFF_CLIENT_IP_HEADER" )
Environment variables read by Load.
const ( DefaultAddr = ":8100" DefaultLogLevel = "info" DefaultDataDir = "var" // DefaultSourceACPR keeps the ACPR blacklist off: its reuse licence is // still to be obtained (docs/sources.md). DefaultSourceACPR = "false" // DefaultProblemBaseURL is where arnakoff/web documents the problems // the API answers, one page per code. DefaultProblemBaseURL = "https://arnakoff.com/docs/erreurs/" )
Defaults applied by Load to an unset variable. ARNAKOFF_LOG_KEY has none.
const ( // DefaultBatchMaxItems is the most items POST /v1/check/batch accepts, // as the owner set it; the contract's example states it. DefaultBatchMaxItems = 50 // MaxBatchMaxItems is the highest value Load accepts: beyond, one // request would weigh like a thousand checks. MaxBatchMaxItems = 1000 )
Bounds of ARNAKOFF_BATCH_MAX_ITEMS.
const ( // DefaultInstances is one process: a single API, or a command run by // hand. DefaultInstances = 1 // MaxInstances is the highest value Load accepts: beyond, each process // would get less than one SIRENE request a second, and a limit shared // among them would serve better (#336). MaxInstances = 7 )
Bounds of ARNAKOFF_INSTANCES.
const ( EnvDemoOrigin = "ARNAKOFF_DEMO_ORIGIN" EnvTurnstileSecret = "ARNAKOFF_TURNSTILE_SECRET" //nolint:gosec // G101: the name of a variable, not its value. EnvRateLimitDemo = "ARNAKOFF_RATE_LIMIT_DEMO" EnvRateLimitDemoGlobal = "ARNAKOFF_RATE_LIMIT_DEMO_GLOBAL" )
Environment variables of the demo access, read by Load.
const ( // DefaultRateLimitDemo is the number of demo checks of a client // address. DefaultRateLimitDemo = 10 // DefaultRateLimitDemoGlobal is the number of demo checks of every // client together: a flood from many addresses stays bounded. DefaultRateLimitDemoGlobal = 300 )
Defaults of the demo limits, in requests per minute, as the owner set them (#60).
const ( EnvSMTPAddr = "ARNAKOFF_SMTP_ADDR" EnvSMTPUsername = "ARNAKOFF_SMTP_USERNAME" EnvSMTPPassword = "ARNAKOFF_SMTP_PASSWORD" //nolint:gosec // G101: the name of a variable, not its value. EnvMailFrom = "ARNAKOFF_MAIL_FROM" EnvLoginLinkURL = "ARNAKOFF_LOGIN_LINK_URL" EnvRateLimitKeyRequest = "ARNAKOFF_RATE_LIMIT_KEY_REQUEST" EnvRateLimitKeyRequestAddress = "ARNAKOFF_RATE_LIMIT_KEY_REQUEST_ADDRESS" EnvRateLimitKeyRequestGlobal = "ARNAKOFF_RATE_LIMIT_KEY_REQUEST_GLOBAL" )
Environment variables of the key requests by login link, read by Load.
const ( // DefaultMailFrom is the sending mailbox the owner chose // (arnakoff/.github#3). DefaultMailFrom = "contact@arnakoff.com" // DefaultLoginLinkURL is the page of the site a login link opens. DefaultLoginLinkURL = "https://arnakoff.com/cle" // DefaultRateLimitKeyRequest is the number of key requests and // redemptions per minute of a client address. DefaultRateLimitKeyRequest = 5 // DefaultRateLimitKeyRequestAddress is the number of login links an // email address may receive per hour. DefaultRateLimitKeyRequestAddress = 3 // DefaultRateLimitKeyRequestGlobal is the number of login links mailed // per hour to every address together, as the owner set it (#245, // 06/11/2026): a flood that solves Turnstile from many client addresses // spends at most 720 a day of the 10,000 emails a month of Scaleway // Transactional Email. It is shared among the instances (KeyRequest.Share). DefaultRateLimitKeyRequestGlobal = 30 )
Defaults of the key requests.
const ( DefaultRateLimitKey = 60 DefaultRateLimitIP = 30 // DefaultRateLimitAuth is the number of refused API keys a minute of // a client address, as the owner set it (#197). DefaultRateLimitAuth = 30 // MaxRateLimit is the highest limit Load accepts. MaxRateLimit = 10000 )
Defaults of the rate limits, in requests per minute, as the owner set them until the offers of #80.
const ( EnvRestoreURL = "ARNAKOFF_RESTORE_URL" EnvRestoreIdentity = "ARNAKOFF_RESTORE_IDENTITY" )
Environment variables of the restore of the key store in production (#297), read by Load.
const ( EnvRotateAccessKey = "ARNAKOFF_ROTATE_ACCESS_KEY" EnvRotateSecretKey = "ARNAKOFF_ROTATE_SECRET_KEY" EnvRotateOrganizationID = "ARNAKOFF_ROTATE_ORGANIZATION_ID" EnvRotateProjectID = "ARNAKOFF_ROTATE_PROJECT_ID" EnvRotateBucket = "ARNAKOFF_ROTATE_BUCKET" EnvRotateAppAPI = "ARNAKOFF_ROTATE_APP_API" EnvRotateAppSync = "ARNAKOFF_ROTATE_APP_SYNC" EnvRotateAppCI = "ARNAKOFF_ROTATE_APP_CI" EnvRotateAppSelf = "ARNAKOFF_ROTATE_APP_SELF" EnvRotateAppMail = "ARNAKOFF_ROTATE_APP_MAIL" EnvRotateSMTPAddr = "ARNAKOFF_ROTATE_SMTP_ADDR" EnvRotateSMTPUsername = "ARNAKOFF_ROTATE_SMTP_USERNAME" EnvRotateAPIContainerID = "ARNAKOFF_ROTATE_API_CONTAINER_ID" EnvRotateSyncAccessSecret = "ARNAKOFF_ROTATE_SYNC_ACCESS_SECRET_ID" EnvRotateSyncSecretSecret = "ARNAKOFF_ROTATE_SYNC_SECRET_SECRET_ID" EnvRotateSelfAccessSecret = "ARNAKOFF_ROTATE_SELF_ACCESS_SECRET_ID" EnvRotateSelfSecretSecret = "ARNAKOFF_ROTATE_SELF_SECRET_SECRET_ID" EnvGitHubAppClientID = "ARNAKOFF_GITHUB_APP_CLIENT_ID" EnvGitHubAppInstallationID = "ARNAKOFF_GITHUB_APP_INSTALLATION_ID" EnvGitHubAppPrivateKey = "ARNAKOFF_GITHUB_APP_PRIVATE_KEY" )
Environment variables of the key rotation job (ADR 0007), read by Load.
const ( EnvSnapshotBucket = "ARNAKOFF_SNAPSHOT_BUCKET" EnvSnapshotBucketAccessKey = "ARNAKOFF_SNAPSHOT_BUCKET_ACCESS_KEY" EnvSnapshotBucketSecretKey = "ARNAKOFF_SNAPSHOT_BUCKET_SECRET_KEY" //nolint:gosec // G101: the name of a variable, not its value. )
Environment variables of the snapshot bucket, read by Load.
const DefaultAPIKeyEnv = "live"
DefaultAPIKeyEnv accepts the production keys, "ark_live_…".
const EnvEraseEmail = "ARNAKOFF_ERASE_EMAIL"
EnvEraseEmail is the address of the account admin account erase -listen erases in production (#304), read by Load.
const MinLogKeyBytes = logging.MinKeyBytes
MinLogKeyBytes is the shortest log key Load accepts, once hex-decoded: the minimum of logging.New.
Variables ¶
var ErrInvalid = errors.New("invalid configuration")
ErrInvalid means a variable holds a value Load cannot use.
Functions ¶
This section is empty.
Types ¶
type Backup ¶ added in v0.57.0
type Backup struct {
// Bucket is the bucket name, as SnapshotBucket.Name.
Bucket string
// AccessKey and SecretKey are the credentials of the bucket, which may
// only add objects. Both are Secrets, kept out of every log.
AccessKey Secret
SecretKey Secret
// Recipient is the age public key the backups are sealed for. It is
// public: no Secret. Its format is checked where it is used.
Recipient string
}
Backup is where admin backup keeps the sealed copies of the key store: a bucket out of Scaleway, and the public key they are sealed for. Every field is empty when unset; RequireBackup tells whether it is complete.
type Config ¶
type Config struct {
// Addr is the TCP address the HTTP API listens on, "host:port" with a
// numeric port; an empty host means every interface.
Addr string
// LogLevel is the minimum level written to the log.
LogLevel slog.Level
// LogKey is the HMAC key that pseudonymises personal data in the log.
LogKey Secret
// DataDir is the only directory the commands write to: the data
// volume. Every other path may be read-only.
DataDir string
// SourceACPR turns the ACPR blacklist on, once its reuse licence is
// obtained.
SourceACPR bool
// AlertWebhook is the HTTPS URL a failed or rejected sync is posted
// to; nil when unset or empty, and then alerts are only logged. It is a
// Secret: a webhook URL usually carries its credential.
AlertWebhook Secret
// ProblemBaseURL is the HTTPS URL of the documentation of the API's
// problems, ending with a slash: the type of a problem is its code
// under it.
ProblemBaseURL string
// BatchMaxItems is the most items POST /v1/check/batch accepts, from 1
// to MaxBatchMaxItems.
BatchMaxItems int
// Instances is the most processes of the API running at once, from 1
// to MaxInstances. The registries limit requests per IP address, and the
// platform does not tell whether the processes share one: each keeps to
// its share of the limit.
Instances int
// DatabaseURL is the PostgreSQL connection string of the API key store;
// nil when unset or empty. RequireKeyStore tells whether it is set.
DatabaseURL Secret
// DatabaseCA is the certificate authority of the database, in PEM,
// which replaces the system's to verify it: that of a managed database
// instance. Empty when unset. It is public: no Secret.
DatabaseCA string
// APIKeySecret is the HMAC key the API keys are digested with, kept
// out of the database; nil when unset or empty.
APIKeySecret Secret
// APIKeyEnv is the environment of the API keys accepted: "live" or
// "test".
APIKeyEnv string
// RateLimitKey is the number of requests per minute of an API key,
// from 1 to MaxRateLimit.
RateLimitKey int
// RateLimitIP is the number of requests per minute of a client address
// on the routes needing no key, from 1 to MaxRateLimit.
RateLimitIP int
// RateLimitAuth is the number of refused API keys per minute of a
// client address on the check routes, from 1 to MaxRateLimit: over it,
// its keys are no longer looked up.
RateLimitAuth int
// TrustedProxies are the proxies whose X-Forwarded-For field tells the
// client's address: nil when unset, empty when set to "none".
// RequireTrustedProxies tells whether it is set.
TrustedProxies []netip.Prefix
// ClientIPHeader is the field, in canonical form, that the platform in
// front of the API overwrites with the client's address, such as
// X-Real-Ip on Scaleway Serverless Containers; empty when unset. It
// excludes TrustedProxies.
ClientIPHeader string
// Demo configures the demo access of the site's checker.
Demo Demo
// KeyRequest configures the free API keys requested by login link.
KeyRequest KeyRequest
// SnapshotBucket is the bucket the snapshots are stored in instead of
// SnapshotDir, when it is enabled.
SnapshotBucket SnapshotBucket
// Backup is where admin backup keeps the sealed copies of the key
// store.
Backup Backup
// Restore is the dump admin restore -listen loads in production.
Restore Restore
// Rotate configures the key rotation job (ADR 0007).
Rotate Rotate
// Erase is the account admin account erase erases (#304).
Erase Erase
}
Config is the configuration of every command.
func Load ¶
Load reads the configuration with lookup, such as os.LookupEnv. A variable that is set, even to an empty string, is used as is; an unset one takes its default. It returns every invalid variable at once, each wrapping ErrInvalid; an error never quotes the value of a Secret.
func (Config) RequireBackup ¶ added in v0.57.0
RequireBackup returns an error, wrapping ErrInvalid and naming each missing variable, unless the database and the backup are configured.
func (Config) RequireDatabase ¶ added in v0.39.0
RequireDatabase returns an error, wrapping ErrInvalid, unless the database of the API key store is set.
func (Config) RequireErase ¶ added in v0.60.0
RequireErase returns an error, wrapping ErrInvalid and naming each missing variable, unless the database and the address to erase are configured.
func (Config) RequireKeyStore ¶ added in v0.39.0
RequireKeyStore returns an error, wrapping ErrInvalid, unless the API key store is configured: a database and an API key secret other than the log key, so that a leaked log key does not also let anyone check a key.
func (Config) RequireRestore ¶ added in v0.58.0
RequireRestore returns an error, wrapping ErrInvalid and naming each missing variable, unless the database and the restore are configured.
func (Config) RequireRotate ¶ added in v0.59.0
RequireRotate returns an error, wrapping ErrInvalid and naming each missing variable, unless the rotation job is configured.
func (Config) RequireTrustedProxies ¶ added in v0.40.0
RequireTrustedProxies returns an error, wrapping ErrInvalid, when the API serves live keys without ARNAKOFF_TRUSTED_PROXIES nor ARNAKOFF_CLIENT_IP_HEADER: the operator must say who tells the client's address, or that no proxy does.
func (Config) SnapshotDir ¶
SnapshotDir is where the validated snapshots of the sources are stored, inside DataDir, unless SnapshotBucket is enabled.
type Demo ¶ added in v0.42.0
type Demo struct {
// Origin is the site's origin, "https://host", the only one CORS
// allows; empty when the demo is off.
Origin string
// TurnstileSecret is the secret key of the site's Turnstile widget;
// nil when the demo is off.
TurnstileSecret Secret
// RateLimit is the number of demo checks per minute of a client
// address, from 1 to MaxRateLimit.
RateLimit int
// RateLimitGlobal is the number of demo checks per minute of every
// client together, from 1 to MaxRateLimit.
RateLimitGlobal int
}
Demo configures the demo access of the site's checker: checks without an API key, from the site only, each proven by a Cloudflare Turnstile token.
type Erase ¶ added in v0.60.0
type Erase struct {
// Email is the address of the account: personal data, a Secret so
// that no log shows it; its format is checked where it is used.
Email Secret
}
Erase is the account admin account erase erases.
type GitHubApp ¶ added in v0.59.0
type GitHubApp struct {
ClientID string
InstallationID int64
// PrivateKey is its private key, in PEM, checked where it is used.
PrivateKey Secret
}
GitHubApp is the GitHub App the job writes environment secrets as.
type KeyRequest ¶ added in v0.52.0
type KeyRequest struct {
// SMTPAddr is the "host:port" of the sending server, which speaks TLS
// at once; empty when the key requests are off.
SMTPAddr string
// SMTPUsername and SMTPPassword authenticate with the server.
SMTPUsername string
SMTPPassword Secret
// MailFrom is the sender address, a bare address.
MailFrom string
// LinkURL is the HTTPS page of the site a login link opens, the token
// in its fragment.
LinkURL string
// RateLimit is the number of requests per minute of a client address
// to the key request routes, from 1 to MaxRateLimit.
RateLimit int
// RateLimitAddress is the number of login links per hour an email
// address may receive, from 1 to MaxRateLimit.
RateLimitAddress int
// RateLimitGlobal is the number of login links per hour mailed to
// every address together, from 1 to MaxRateLimit.
RateLimitGlobal int
}
KeyRequest configures the free API keys requested by login link (#59).
func (KeyRequest) Enabled ¶ added in v0.52.0
func (k KeyRequest) Enabled() bool
Enabled reports whether the key requests are on.
func (KeyRequest) Share ¶ added in v0.62.2
func (k KeyRequest) Share(instances int) KeyRequest
Share returns k with the mail limits split among instances processes running at once, each counting in its own memory: together they mail no more than the configured hourly figures (#344). Each share is at least one. The per-minute limit of a client address is left whole, as every other per-address limit.
type Restore ¶ added in v0.58.0
type Restore struct {
// URL is the presigned URL of the sealed dump: its query is a
// credential.
URL Secret
// Identity is the age private key the dump is sealed for, drawn for
// this restore only.
Identity Secret
}
Restore is the dump admin restore -listen loads: a presigned URL of the dump, sealed for a key drawn for this restore, and that key. Both are Secrets, kept out of every log; their format is checked where they are used. RequireRestore tells whether it is complete.
type Rotate ¶ added in v0.59.0
type Rotate struct {
// AccessKey and SecretKey are the key of the job itself.
AccessKey string
SecretKey Secret
// OrganizationID is the organization of the applications, which the
// API requires to list their keys; ProjectID is the default project of
// the keys it creates.
OrganizationID string
ProjectID string
// Bucket is the snapshot bucket a new key of api or sync must read.
Bucket string
// Apps are the IAM applications of the closed list.
Apps RotateApps
// APIContainerID is the api container, whose secrets hold its key.
APIContainerID string
// SyncSecrets and SelfSecrets are the Secret Manager secrets the sync
// job and the rotation job read their key from, one per half.
SyncSecrets, SelfSecrets SecretIDs
// GitHubApp writes the key of ci-deploy to the GitHub environment.
GitHubApp GitHubApp
// SMTP is the server a new key of mail must authenticate with.
SMTP RotateSMTP
}
Rotate is the configuration of the key rotation job: its own Scaleway key, the closed list of applications whose keys it rotates and where it puts them. Every field is empty when unset; RequireRotate tells whether it is complete. Identifiers are UUIDs, checked by Load.
type RotateApps ¶ added in v0.59.0
type RotateApps struct {
API, Sync, CI, Self, Mail string
}
RotateApps are the IAM applications whose keys the job rotates.
type RotateSMTP ¶ added in v0.60.1
type RotateSMTP struct {
Addr, Username string
}
RotateSMTP is the SMTP server of the mail application (#295): its "host:port", which speaks TLS at once, and the username its keys authenticate as.
type Secret ¶
type Secret []byte
Secret is a configuration value that must never reach a log, an error or a response. Every fmt verb, encoding/json and log/slog write it as "[redacted]"; only Bytes gives the value.
func (Secret) MarshalText ¶
MarshalText implements encoding.TextMarshaler, which encoding/json uses.
type SecretIDs ¶ added in v0.59.0
type SecretIDs struct {
Access, Secret string
}
SecretIDs are the two secrets of a key: its access key and its secret key.
type SnapshotBucket ¶ added in v0.50.0
type SnapshotBucket struct {
// Name is the bucket: 3 to 63 lower-case letters, digits and dashes,
// starting and ending with a letter or a digit.
Name string
// AccessKey and SecretKey are the credentials of the bucket: the key
// pair of an IAM application restricted to it. Both are Secrets, kept
// out of every log.
AccessKey Secret
SecretKey Secret
}
SnapshotBucket is the S3 bucket the snapshots are stored in, in production (ADR 0005); off when its name is empty, and then they stay in SnapshotDir.
func (SnapshotBucket) Enabled ¶ added in v0.50.0
func (b SnapshotBucket) Enabled() bool
Enabled reports whether the snapshots are stored in the bucket.