Documentation
¶
Overview ¶
Package app wires the reference server: storage, the MDM core, the Declarative Device Management engine, the adapters between the roles, the change notifier, a health endpoint, and a minimal admin API.
Why ¶
The library packages are deliberately independent; something has to assemble them into a process that CI can build into a container and run on the far side of a real network hop. Phase 5 of the plan of record brings that assembly forward in minimal form so E2E-010 (split deployment) exercises our own binary on both sides of the wire; phase 8 grows it into the documented reference server. Three roles exist: "mdm" serves check-in and connect and reaches DDM either in-process or through ddm/adapter/proxyclient, "ddm" serves the engine behind ddm/adapter/proxyserver plus the admin API, and "all" runs both in one process. Roles that split across processes share one storage DSN. Push delivery and TLS termination are left to the deployment (phase 8).
References ¶
- Decision record 0023: docs/research/decisions/0023-ddm-adapters-and-wire-contract.md
- Decision record 0025: docs/research/decisions/0025-reference-server-roles-and-container.md
- Plan of record: docs/research/implementation_plan.md (phase 5, phase 8)
- Threat model: docs/security/threat-model.md (trust boundaries 5 and 6)
- E2E scenarios: docs/testing/e2e-scenarios.md (E2E-010, E2E-015)
- Container: Dockerfile, scripts/testdb.sh (ddm-up)
Index ¶
- Constants
- Variables
- func ACMEKeyFromEnvForTests(v string) ([]byte, error)
- func ACMEStatusForTests(err error) int
- func AdminActions() []adminauth.Action
- func AxMStatusForTests(err error) int
- func CertificateFromPEM(data []byte) (*x509.Certificate, error)
- func DEPStatusForTests(err error) int
- func ParseDiscovery(s string) (map[discovery.ModelFamily]string, error)
- type ACMEConfig
- type App
- func (a *App) ACMEIdentifierForTests(b acme.Binding) (string, error)
- func (a *App) ACMEStoreForTests() acme.Store
- func (a *App) AdminRoutes() []adminRoute
- func (a *App) Close() error
- func (a *App) DEPStoreForTests() dep.Store
- func (a *App) Run(ctx context.Context) error
- func (a *App) Workers() []WorkerState
- type AxMConfig
- type Config
- type DEPConfig
- type EnrollConfig
- type OIDCConfig
- type PushConfig
- type Role
- type SinkConfig
- type WorkerState
Constants ¶
const ( // IdentitySCEP is the default: the profile carries a SCEP payload and // a challenge password. IdentitySCEP = "scep" // IdentityACME issues the identity through ACME with Managed Device // Attestation, which needs no secret in the profile. IdentityACME = "acme" )
Identity methods for the enrollment profile.
const ( // ACMEPolicyAny issues to any device that produced a valid attestation // for a recognised client identifier. ACMEPolicyAny = "any" // ACMEPolicyDEP issues only to devices assigned to this organisation in // the device enrollment service. ACMEPolicyDEP = "dep" // ACMEPolicySIP additionally requires System Integrity Protection. ACMEPolicySIP = "sip" )
ACME policies selectable from the environment.
const ( ActionPutDeclaration = "putDeclaration" ActionGetDeclaration = "getDeclaration" ActionDeleteDeclaration = "deleteDeclaration" ActionAssignSet = "assignSet" ActionReadEnrollment = "readEnrollment" ActionReadEnrollmentStatus = "readEnrollmentStatus" ActionNotify = "notify" ActionManageDEP = "manageDEP" ActionManageBusinessMgr = "manageBusinessManager" ActionReadACME = "readACME" ActionManagePrincipals = "managePrincipals" ActionReadAudit = "readAudit" ActionDisableEnrollment = "disableEnrollment" ActionEnqueueCommand = "enqueueCommand" ActionReadCommands = "readCommands" ActionClearCommands = "clearCommands" ActionPushEnrollment = "pushEnrollment" ActionManagePushCerts = "managePushCertificates" ActionExportEnrollments = "exportEnrollments" ActionImportEnrollments = "importEnrollments" ActionManagePolicies = "managePolicies" )
Admin action ids. Every admin route declares one, and the set below is the registry a stored policy is validated against, so a policy naming an action that no route serves is refused when it is written rather than silently never granting (decision record 0034).
const ( PathMDM = "/mdm" PathDDM = "/ddm" PathHealthz = "/healthz" PathAdmin = "/admin/v1/" )
Paths served by the handler.
const ( PathSCEP = "/scep" PathACME = "/acme" PathACMECredential = "/enroll/acme-credential" // #nosec G101 -- a route, not a credential PathWellKnown = discovery.WellKnownPath PathEnroll = "/enroll/" // + discovery version (mdm-byod, mdm-adde) PathADE = "/enroll/ade" // DEP profile url and configuration_web_url PathAuthenticate = "/enroll/authenticate" // apple-as-web web-auth URL PathOAuth2Authorize = "/enroll/oauth2/authorize" PathOAuth2Token = "/enroll/oauth2/token" // #nosec G101 -- a route, not a credential PathOIDCCallback = "/enroll/oidc/callback" OAuth2ClientID = "mdm" OAuth2RedirectURL = accountdriven.CallbackScheme + ":/oauth2/redirection" OAuth2Scope = "MDM" )
Enrollment routes on the mdm and all roles (decision records 0027 to 0029).
const ( EnvRole = "MDM_ROLE" EnvListen = "MDM_LISTEN" EnvStorage = "MDM_STORAGE" EnvDSN = "MDM_DSN" EnvDDMURL = "MDM_DDM_URL" EnvDDMSendKey = "MDM_DDM_SEND_KEY" EnvDDMRecvKey = "MDM_DDM_RECV_KEY" EnvAdminToken = "MDM_ADMIN_TOKEN" // #nosec G101 -- the variable name, not a credential // EnvAdminStore opens the admin principal and policy store on the // process's own database. Off by default: it mounts the admin API. EnvAdminStore = "MDM_ADMIN_STORE" // EnvAudit writes a projected slog record for every event. EnvAudit = "MDM_AUDIT_LOG" // EnvWebhookURL receives an event per POST in the MicroMDM envelope. EnvWebhookURL = "MDM_WEBHOOK_URL" // EnvAuditStore persists every event to the audit trail. EnvAuditStore = "MDM_AUDIT_STORE" // EnvAuditRetention is how long audit records are kept. EnvAuditRetention = "MDM_AUDIT_RETENTION" // EnvWebhookHMACKey signs the webhook body. EnvWebhookHMACKey = "MDM_WEBHOOK_HMAC_KEY" // #nosec G101 -- the variable name, not a credential EnvCAFile = "MDM_CA_FILE" EnvCertHeader = "MDM_CERT_HEADER" // Enrollment routes (EnrollConfig). EnvPublicURL = "MDM_PUBLIC_URL" EnvPushTopic = "MDM_PUSH_TOPIC" EnvEnrollCACertFile = "MDM_ENROLL_CA_CERT_FILE" EnvEnrollCAKeyFile = "MDM_ENROLL_CA_KEY_FILE" EnvSCEPChallenge = "MDM_SCEP_CHALLENGE" // #nosec G101 -- the variable name, not a credential EnvSCEPHMACKey = "MDM_SCEP_HMAC_KEY" // #nosec G101 -- the variable name, not a credential EnvProfileIdentifier = "MDM_PROFILE_IDENTIFIER" EnvOrganization = "MDM_ORGANIZATION" EnvDiscovery = "MDM_DISCOVERY" EnvAccountDrivenMethod = "MDM_ACCOUNT_DRIVEN_METHOD" EnvOIDCIssuer = "MDM_OIDC_ISSUER" EnvOIDCClientID = "MDM_OIDC_CLIENT_ID" EnvOIDCClientSecret = "MDM_OIDC_CLIENT_SECRET" // #nosec G101 -- the variable name, not a credential EnvADEAnchorFile = "MDM_ADE_ANCHOR_FILE" EnvADEAudit = "MDM_ADE_AUDIT" EnvRequireUserAuth = "MDM_REQUIRE_USER_AUTH" // Apple Business Manager (AxMConfig). EnvAxMClientID = "MDM_AXM_CLIENT_ID" EnvAxMKeyID = "MDM_AXM_KEY_ID" EnvAxMKeyFile = "MDM_AXM_KEY_FILE" EnvAxMScope = "MDM_AXM_SCOPE" EnvAxMBaseURL = "MDM_AXM_BASE_URL" EnvAxMTokenURL = "MDM_AXM_TOKEN_URL" // #nosec G101 -- the variable name, not a credential // Device enrollment service (DEPConfig). EnvDEPBaseURL = "MDM_DEP_BASE_URL" EnvDEPSyncInterval = "MDM_DEP_SYNC_INTERVAL" EnvDEPAssignInterval = "MDM_DEP_ASSIGN_INTERVAL" // #nosec G101 -- the variable name, not a credential EnvDEPProfileURL = "MDM_DEP_PROFILE_URL" EnvDEPUsePUT = "MDM_DEP_USE_PUT" // Push: where APNs credentials come from, and how pushes are shaped. EnvPushSource = "MDM_PUSH_SOURCE" EnvPushCertFile = "MDM_PUSH_CERT_FILE" EnvPushKeyFile = "MDM_PUSH_KEY_FILE" EnvPushHost = "MDM_PUSH_HOST" EnvPushCoalesce = "MDM_PUSH_COALESCE" EnvPushCertTTL = "MDM_PUSH_CERT_TTL" // ACME and Managed Device Attestation (ACMEConfig). EnvIdentity = "MDM_IDENTITY" EnvACMEPolicy = "MDM_ACME_POLICY" EnvACMEKey = "MDM_ACME_KEY" EnvACMEHMACKey = "MDM_ACME_HMAC_KEY" // #nosec G101 -- the variable name, not a credential EnvACMEAnchorFile = "MDM_ACME_ANCHOR_FILE" EnvACMEUnattested = "MDM_ACME_ALLOW_UNATTESTED" EnvACMEIdentTTL = "MDM_ACME_IDENTIFIER_TTL" EnvSubscriptions = "MDM_DDM_SUBSCRIPTIONS" )
Environment variables read by ParseEnv.
const ( DefaultRole = RoleAll DefaultListen = ":8080" DefaultStorage = "sqlite" DefaultDSN = "mdm.db" )
Defaults applied by ParseEnv when a variable is unset.
const ( // PushSourceOff sends no pushes. A declaration change still queues a // command, which the device collects on its next check-in. PushSourceOff = "off" // PushSourceFile loads one certificate and key from disk. PushSourceFile = "file" // PushSourceStore loads certificates from the push certificate store, // reloading when an admin uploads a new one. PushSourceStore = "store" )
Push certificate sources.
const ActionReadConfig = "readConfig"
ActionReadConfig is declared by the introspection routes. They are authenticated but not policy-gated (adminRoute.Introspection), so the action exists for the route table's sake rather than to be granted.
const BreakGlassActor = "break-glass"
BreakGlassActor is the audit actor for a request authenticated by the static MDM_ADMIN_TOKEN rather than by a stored principal. It is a fixed string, so an operator can alert on it: after the first principals exist, a record carrying this actor means someone used the standing root credential that should have been removed.
const DefaultAuditPruneInterval = time.Hour
DefaultAuditPruneInterval is how often the retention worker runs.
const DefaultAuditRetention = 90 * 24 * time.Hour
DefaultAuditRetention is how long records are kept when a retention is configured without a window. Long enough to investigate an incident reported weeks late, short enough that the table is not a liability.
const DefaultPushCoalesce = 5 * time.Second
DefaultPushCoalesce collapses repeated pushes to one enrollment inside this window, so a burst of declaration changes wakes a device once.
const MaxAdminBody = 1 << 20
MaxAdminBody bounds admin request bodies.
const MaxAxMSerials = 1000
MaxAxMSerials bounds one assignment request.
Variables ¶
var ( ErrBodyTooLarge = errors.New("app: body too large") ErrBadChannel = errors.New("app: channel must be device or user") )
Admin API errors.
var ( // ErrForbidden is a caller authenticated but not permitted. ErrForbidden = errors.New("app: forbidden") // ErrAdminUnconfigured is an admin API with neither a principal store nor // a static token. It is a Build error rather than a silently disabled // API, so a deployment cannot believe it is serving one when it is not // (decision record 0034). ErrAdminUnconfigured = errors.New("app: admin API needs MDM_ADMIN_TOKEN or an admin principal store") )
Admin authorization errors.
var ( AxMWaitInterval = 2 * time.Second AxMWaitTimeout = 5 * time.Minute )
Waits used by the admin API when the caller asks to wait.
var ErrBadACMERequest = errors.New("app: invalid ACME request")
ErrBadACMERequest is a malformed admin request.
var ErrBadAxMRequest = errors.New("app: invalid Business Manager request")
ErrBadAxMRequest reports an invalid assignment body.
var (
ErrBadDEPRequest = errors.New("app: invalid DEP request")
)
DEP errors.
var ErrConfig = errors.New("app: invalid configuration")
ErrConfig reports an invalid configuration.
Functions ¶
func ACMEKeyFromEnvForTests ¶
ACMEKeyFromEnvForTests exposes the key reader to tests of the wiring.
func ACMEStatusForTests ¶
ACMEStatusForTests exposes the error mapping to tests of the wiring.
func AdminActions ¶
AdminActions describes every action, with operator-facing prose naming the consequence. `mdmctl policy actions` prints these, so an operator granting an action knows what they are granting rather than guessing from its name.
func AxMStatusForTests ¶
AxMStatusForTests exposes the error mapping to tests of the wiring.
func CertificateFromPEM ¶
func CertificateFromPEM(data []byte) (*x509.Certificate, error)
CertificateFromPEM reads the first certificate of a PEM bundle; the admin keypair route returns one for the portal upload.
func DEPStatusForTests ¶
DEPStatusForTests exposes the error mapping to tests of the wiring.
func ParseDiscovery ¶
func ParseDiscovery(s string) (map[discovery.ModelFamily]string, error)
ParseDiscovery reads "Mac=mdm-adde,iPhone=mdm-byod".
Types ¶
type ACMEConfig ¶
type ACMEConfig struct {
// Policy is which devices may enroll: ACMEPolicyAny, ACMEPolicyDEP, or
// ACMEPolicySIP. Empty means ACMEPolicyAny.
Policy string
// AllowUnattested issues on the client identifier alone to a device
// that produced no attestation. Off by default.
AllowUnattested bool
// KeyType and KeySize are what the profile asks the device to generate.
// Empty means an attestable elliptic curve key of 384 bits.
KeyType string
KeySize int64
// HMACKey mints client identifiers. Empty falls back to the SCEP HMAC
// key, and then to a key generated at startup, which is fine for one
// process and wrong for several.
HMACKey []byte
// IdentifierTTL is how long a client identifier in a profile stays
// usable. It has to cover the gap between handing a device its profile
// and the device acting on it.
IdentifierTTL time.Duration
// AnchorFile is a PEM bundle of attestation anchors, for a lab with a
// simulated device. Empty trusts Apple alone.
AnchorFile string
// Anchors is the parsed AnchorFile; tests set it directly.
Anchors []*x509.Certificate
// Store overrides the ACME store; default follows the storage backend.
Store acme.Store
// NonceTTL and OrderTTL default to the acme package's values.
NonceTTL, OrderTTL time.Duration
}
ACMEConfig configures the ACME server and the identities it issues.
type App ¶
type App struct {
Handler http.Handler
Core *service.Core
Engine *ddm.Engine
Notifier *ddm.Notifier
Store storage.Store
// AxM is the Business Manager client when configured.
AxM *axm.Client
// DEP is the device enrollment service; nil on the mdm role.
DEP *dep.Client
// Push wakes devices; nil when no push source is configured.
Push *push.Notifier
// contains filtered or unexported fields
}
App is a built process.
func (*App) ACMEIdentifierForTests ¶
ACMEIdentifierForTests mints a client identifier, so a test can order without composing a whole enrollment profile.
func (*App) ACMEStoreForTests ¶
ACMEStoreForTests exposes the ACME store to tests of the wiring.
func (*App) AdminRoutes ¶
func (a *App) AdminRoutes() []adminRoute
AdminRoutes returns the mounted admin route table: pattern, action, and family, with no handlers. It is what GET /routes serves and what mdmctl reads to explain a 404.
func (*App) Close ¶
Close releases storage, draining the event bus first when Build created it, so an asynchronous sink finishes delivering before the process exits.
func (*App) DEPStoreForTests ¶
DEPStoreForTests exposes the DEP store to tests of the wiring.
func (*App) Run ¶
Run supervises every registered background loop until ctx is cancelled or one of them fails, whichever comes first. The HTTP listener is the caller's (cmd/mdmserver, or httptest in tests).
The first failure cancels its siblings so Run returns promptly rather than waiting for loops that only stop on cancellation. A loop that stops because the context ended is not a failure, and the two existing loops disagree on how they say so -- ddm.Notifier.Run returns ctx.Err(), depService.Run returns nil -- so cancellation is normalised here rather than in each loop.
func (*App) Workers ¶
func (a *App) Workers() []WorkerState
Workers reports every supervised loop and whether it is running, in registration order. Readiness reads this; a worker that has stopped while the process keeps serving is exactly the state /healthz could not see.
type AxMConfig ¶
type AxMConfig struct {
ClientID string
KeyID string
// KeyFile is the PEM private key downloaded from the portal; KeyPEM is
// its content (tests set it directly).
KeyFile string
KeyPEM []byte
// Scope, BaseURL, and TokenURL override the defaults derived from the
// client id (tests point them at a fake).
Scope, BaseURL, TokenURL string
HTTPClient *http.Client
}
AxMConfig connects the reference server to Apple Business Manager or Apple School Manager (decision record 0030). Inactive until ClientID and KeyID are set with a key.
type Config ¶
type Config struct {
Role Role
Listen string
Storage string // sqlite, postgres, mysql, inmem
DSN string // file path for sqlite
// DDMURL, on the mdm role, forwards DeclarativeManagement check-ins to
// a ddm role through proxyclient; empty means the local engine.
DDMURL string
// DDMSendKey signs what this role sends across the hop; DDMRecvKey
// verifies what it receives.
DDMSendKey, DDMRecvKey []byte
// AdminToken enables the admin API on the ddm and all roles with a single
// static credential that authenticates as root and bypasses policy.
//
// Alongside a principal store it is the break-glass credential, and it
// keeps working rather than being superseded: an empty principal store
// authenticates nobody, and the route that creates the first principal is
// itself authorized, so without it there is no way in. Its use is audited
// under the actor "break-glass" and logged at warn on every request.
//
// It has no expiry and cannot be revoked without restarting the process.
// While it is set, every least-privilege property record 0034 claims is
// void for whoever holds it, so a deployment sets it to create real
// principals and then unsets it. An audit record with the actor
// "break-glass" after that point is an incident. See
// docs/operations/deployment.md.
AdminToken string
// AdminStore holds admin principals and Cedar policies. When set, an admin
// request that does not present AdminToken is authenticated against it and
// authorized by policy (decision record 0034). Injecting a store here
// overrides AdminStoreEnabled, which is how tests supply a fake.
AdminStore adminauth.Store
// AdminStoreEnabled opens the principal and policy store on the process's
// own database, so principals work in the shipped binary rather than only
// where a caller injects AdminStore. Off by default: turning it on mounts
// the admin API, which is a security change rather than a convenience.
AdminStoreEnabled bool
// CAFile is a PEM bundle of roots that device identities chain to;
// the mdm role then verifies Mdm-Signature on every check-in and
// connect. CARoots is the parsed form (tests set it directly).
CAFile string
CARoots *x509.CertPool
// CertHeader names a header carrying the client certificate from a
// TLS-terminating proxy (httpapi.CertFromHeader). Used when no CA is
// configured. With neither, the certificate must come from TLS on
// this process (httpapi.CertFromTLS).
CertHeader string
// Subscriptions enables the synthesised status-subscriptions
// declaration (decision record 0021).
Subscriptions bool
// Enroll turns the enrollment routes on (SCEP, discovery,
// account-driven, ADE).
Enroll EnrollConfig
// AxM connects Apple Business Manager or Apple School Manager; its
// admin routes live under the admin API on the ddm and all roles.
AxM AxMConfig
// DEP configures the device enrollment service client and worker;
// its admin routes live under the admin API too.
DEP DEPConfig
// Push selects where APNs credentials come from. With no source the
// server queues commands and never wakes a device.
Push PushConfig
Logger *slog.Logger
Clock clock.Clock
// Bus carries the typed events every state change publishes. When nil,
// Build creates one so the sinks below have something to subscribe to;
// pass one to observe events from outside the process.
Bus *event.Bus
// Sinks configures what subscribes to the bus.
Sinks SinkConfig
}
Config is the process configuration; see ParseEnv for the MDM_* variables and cmd/mdmserver for the flags.
type DEPConfig ¶
type DEPConfig struct {
// BaseURL overrides https://mdmenrollment.apple.com (tests point it
// at the fake service).
BaseURL string
// SyncInterval and AssignInterval drive the background worker;
// zero disables it (the admin API can still sync and assign).
SyncInterval, AssignInterval time.Duration
// ProfileURL is the DEP profile url; default PublicURL + /enroll/ade.
ProfileURL string
// UsePUT sends PUT for profile assignment (simulators).
UsePUT bool
HTTPClient *http.Client
// Store overrides the DEP store (embedders with their own backend,
// tests with a failing one); default follows Storage.
Store dep.Store
}
DEPConfig connects the reference server to Apple's device enrollment service (decision record 0026). It is always available: accounts are created through the admin API, so nothing is required to enable it.
type EnrollConfig ¶
type EnrollConfig struct {
// PublicURL is the https base devices reach (profiles, discovery,
// redirects).
PublicURL string
// Topic is the APNs topic written into enrollment profiles.
Topic string
// CACertFile and CAKeyFile are the PEM files of the enrollment CA that
// issues device identities through SCEP; both empty means a
// self-signed CA is generated at start (development only) and logged.
CACertFile, CAKeyFile string
// SCEPChallenge is the shared SCEP challenge (development); an HMAC
// challenge derives one-time passwords when SCEPHMACKey is set instead.
SCEPChallenge string
SCEPHMACKey []byte
ProfileIdentifier string
Organization string
// Discovery maps a model family to a discovery version, for example
// Mac=mdm-adde,iPhone=mdm-byod. Families absent are rejected.
Discovery map[discovery.ModelFamily]string
// AccountDrivenMethod is apple-as-web (default) or apple-oauth2.
AccountDrivenMethod string
// OIDC is the identity provider behind the web view, the apple-as-web
// page, and the apple-oauth2 sign-in. Unset leaves only the
// token-based ADE lane.
OIDC OIDCConfig
// ADEAnchorFile is a PEM bundle of MachineInfo signing anchors; empty
// uses Apple's. ADEAudit logs verification failures and continues.
ADEAnchorFile string
ADEAudit bool
// RequireUserAuth gates user channels on UserAuthenticate (0029).
RequireUserAuth bool
// Identity is where an enrolled device's identity certificate comes
// from: IdentitySCEP (the default) or IdentityACME. The ACME endpoints
// are mounted either way, so a declarative credential can use them even
// when enrollment profiles still carry SCEP.
Identity string
// ACME configures the ACME server and the identities it issues.
ACME ACMEConfig
// Anchors is the parsed ADEAnchorFile (tests set it directly).
Anchors []*x509.Certificate
}
EnrollConfig turns the enrollment routes on. It is inactive until PublicURL and Topic are set.
func (EnrollConfig) Enabled ¶
func (e EnrollConfig) Enabled() bool
Enabled reports whether enrollment routes are configured.
type OIDCConfig ¶
type OIDCConfig struct {
Issuer, ClientID, ClientSecret string
// HTTPClient reaches the provider; tests point it at a fake.
HTTPClient *http.Client
}
OIDCConfig is the relying-party configuration.
type PushConfig ¶
type PushConfig struct {
// Source is off, file, or store.
Source string
// CertFile and KeyFile are the PEM pair for the file source.
CertFile, KeyFile string
// Topic is the APNs topic for the file source. Empty derives it from the
// certificate, which is what an operator should prefer: the topic lives
// in the certificate's subject and typing it by hand is how it goes
// wrong.
Topic string
// Host overrides the APNs endpoint, for the lab and for tests.
Host string
// Coalesce is the window repeated pushes collapse into; zero uses
// DefaultPushCoalesce, negative disables coalescing.
Coalesce time.Duration
// CertTTL is how long a store-backed certificate is cached before its
// version is rechecked.
CertTTL time.Duration
// Transport overrides the HTTP client per certificate, for tests.
Transport func(tls.Certificate) *http.Client
// Pusher overrides everything, for tests and for an embedder with its
// own APNs path.
Pusher push.Pusher
}
PushConfig selects where APNs credentials come from.
With no source the notifier is built without a pusher, so a declaration change queues a command and never wakes the device. The device collects it on its next check-in, which for an idle Mac can be hours.
func (PushConfig) Enabled ¶
func (p PushConfig) Enabled() bool
Enabled reports whether pushes are sent.
type SinkConfig ¶
type SinkConfig struct {
// Audit writes a projected slog record for every event. It is the
// cheapest form of the threat model's repudiation control: attributable,
// but only as durable as the log stream it is shipped to.
Audit bool
// WebhookURL receives a POST per event in the MicroMDM envelope, minus
// the raw payload those servers include (event/sink explains why).
WebhookURL string
// WebhookHMACKey signs the webhook body when set.
WebhookHMACKey []byte
// Persist writes every event to the audit trail on the process's own
// database. This is what makes the threat model's repudiation control
// real: an slog record is only as durable as the log stream someone
// remembered to ship, and proving who erased a device three weeks ago
// needs a table.
Persist bool
// AuditStore overrides Persist with a caller's own trail.
AuditStore audit.Store
// Retention is how long records are kept. Zero keeps them forever, which
// is a choice a deployment should make deliberately rather than inherit.
Retention time.Duration
// PruneInterval is how often retention runs; DefaultAuditPruneInterval
// when unset.
PruneInterval time.Duration
}
SinkConfig turns on the event sinks. Both are off by default: an audit log and a webhook are deployment choices, and a library consumer supplies its own subscribers.
func (SinkConfig) Enabled ¶
func (s SinkConfig) Enabled() bool
Enabled reports whether anything subscribes.
type WorkerState ¶
WorkerState reports one supervised loop and whether it is running.