Goal
Close the native device-configuration gaps vs Intune-class tools (Intune Linux is mostly "run a script"; Fleet/JumpCloud/Landscape don't ship native firewall/cert/network actions either; Kandji is Apple-only but its catalog is mined for translatable ideas). Eight new action types + compliance-on-inventory. Design converged 2026-07-03. Scheduled 2026.09 (feature release; 2026.08 is stability/compliance, tracked separately). Decided out: SESSION_LOCK (lock without containment is theater), printer control (backlog, needs a full spec), update-deferral UX (backlog).
Key finding from design review: the SDK muscle largely exists already — sys/firewall (namespace-scoped Manager, nftables + firewalld), sys/dns (resolved + NM), sys/netconfig (multi-address InterfaceConfig, validate-before-mutate), sys/timesync (status + detect, chrony/timedatectl), sys/catrust (cross-distro anchors) — and the proto backend-enum scaffolds are in actions.proto. The work is proto params + server validators + agent executors + small SDK extensions. No parallel manager interfaces; backends bind at construction per the SDK backend pattern.
Action types
FIREWALL (ACTION_TYPE_FIREWALL)
CERT (ACTION_TYPE_CERT)
HOSTNAME (ACTION_TYPE_HOSTNAME) — instant, dispatch-only
TIME (ACTION_TYPE_TIME)
DNS (ACTION_TYPE_DNS)
NETWORK_CONFIG (ACTION_TYPE_NETWORK_CONFIG)
SYSCTL (ACTION_TYPE_SYSCTL)
REMOVABLE_STORAGE (ACTION_TYPE_REMOVABLE_STORAGE)
Compliance on top of inventory
(user direction: derive from inventory, no parallel posture pipeline; today compliance is fully script-based is_compliance SHELL detection)
Triaged out (decided 2026-07-03)
- Printer control → backlog, requires a full spec before any pickup. CUPS quirk surface (localized lpstat/lpadmin needs forced C locale, absent-CUPS handling, driverless-IPP-only, exact-set queue semantics). Queue provisioning via lpadmin
-m everywhere is the v1 shape when it comes back.
- UPDATE enforcement deadlines + user deferral → backlog. Maintenance windows already provide enforcement timing; restart notifications suffice.
- Managed app catalog → marketplace base-template content, not server scope.
- CIS/NIS2 hardening packs → marketplace base-template content (FILE-action definitions), not server scope.
Cross-cutting requirements (apply to every action)
- No accepted-but-ignored backend enum values — ship only implemented values; server validator rejects everything else fail-closed (cf. agent#169).
- No bool whose documented default contradicts the proto3 wire default — name bools so zero value = safe behavior.
- Every new enum has 0 = UNSPECIFIED; every field crossing the boundary carries
@gotags validate.
- SDK-first: extend existing
sys/* packages; backend selected at construction, never per call.
- Redaction-schema entries for every sensitive param; audit events for all state changes.
- Each new action ships its docref-anchored docs page + SDK docs claims.
- Per spec-driven-dev: each action type gets its own spec (acceptance criteria + rejection-paths table) approved before implementation; child issues per repo at work start.
Related
Goal
Close the native device-configuration gaps vs Intune-class tools (Intune Linux is mostly "run a script"; Fleet/JumpCloud/Landscape don't ship native firewall/cert/network actions either; Kandji is Apple-only but its catalog is mined for translatable ideas). Eight new action types + compliance-on-inventory. Design converged 2026-07-03. Scheduled 2026.09 (feature release; 2026.08 is stability/compliance, tracked separately). Decided out: SESSION_LOCK (lock without containment is theater), printer control (backlog, needs a full spec), update-deferral UX (backlog).
Key finding from design review: the SDK muscle largely exists already —
sys/firewall(namespace-scoped Manager, nftables + firewalld),sys/dns(resolved + NM),sys/netconfig(multi-address InterfaceConfig, validate-before-mutate),sys/timesync(status + detect, chrony/timedatectl),sys/catrust(cross-distro anchors) — and the proto backend-enum scaffolds are inactions.proto. The work is proto params + server validators + agent executors + small SDK extensions. No parallel manager interfaces; backends bind at construction per the SDK backend pattern.Action types
FIREWALL (
ACTION_TYPE_FIREWALL)@gotags validate; port ranges (port_start/port_end); backend enum ships NFTABLES + FIREWALLD only (implemented set)sys/firewallnamespaces: manager constructed per action ID;flush= replace rules in this action's namespace only; ABSENT = remove namespacesys/firewall(Manager is rules-only today)CERT (
ACTION_TYPE_CERT)sys/catrust(named anchors, cross-distro) — no hardcoded Debian paths/toolsreload_units(validated unit names) instead of free-formpost_apply_commands— no arbitrary-exec channel inside CERT, no WS8 argv surfaceparams.privateKeyPemin redaction schemas; explicit at-rest decision (accepted-risk like Wi-Fi PSK, or sealed)CertTypegets 0 = UNSPECIFIEDHOSTNAME (
ACTION_TYPE_HOSTNAME) — instant, dispatch-onlyvalidate:"required,hostname_rfc1123"(or FQDN variant); reject CreateAction (all dispatch-surface lockdown as for instant types)hostnamectl set-hostname; systemd required, fail closed/etc/hosts127.0.1.1 entry (stale entry breaks sudo name resolution)TIME (
ACTION_TYPE_TIME)sys/timesyncgains SetTimezone/SetNTP (package is Status-only today) — no raw execs in the agenttimesync.Detect()set_rtc_to_utc→rtc_in_local_timeso the proto3 zero value is the safe default (no comment-vs-wire default lies)validate:"timezone"on the IANA zone; PRESENT-only, documentedDNS (
ACTION_TYPE_DNS)sys/dns(Apply(Config)); backend enum ships RESOLVED + NETWORKMANAGER onlyinterface_nameparam — the NM backend requires it (host-global is resolved-only)optionsconstrained to an allowlist / validated key:value grammar, not free-form stringsNETWORK_CONFIG (
ACTION_TYPE_NETWORK_CONFIG)sys/netconfig.InterfaceConfig: repeated addresses (dual-stack), gateway, DNS, MTU, routes;AddressModegets 0 = UNSPECIFIEDSYSCTL (
ACTION_TYPE_SYSCTL)SysctlParams{ backend, repeated SysctlEntry{key, value} }; key validated against dotted-path grammar (^[a-z0-9_.-]+$-class regex + length caps), value length-capped, entry-count cap; all@gotags validatesys/sysctlwith the standard interface → backend structure:sysctl.New(Backend, runner)— explicit Backend enum even with one implementation today (BackendSysctlD= owned/etc/sysctl.d/drop-in +sysctl --system), zero value invalid,ErrUnknownBackend; Manager with Apply/Remove/Get (Get reads effective/proc/sysvalues for idempotency)pm-<actionId>.conf+ apply live; idempotent by comparing effective values. ABSENT: remove drop-in + re-runsysctl --system; document that keys not covered by remaining config revert only at rebootREMOVABLE_STORAGE (
ACTION_TYPE_REMOVABLE_STORAGE)@gotags validateudevadmreload; idempotency by reading owned files back; container tests at the file levelremovable_storage.blocked)Compliance on top of inventory
(user direction: derive from inventory, no parallel posture pipeline; today compliance is fully script-based
is_complianceSHELL detection)CompliancePolicyRulegains a FACT type alongside SCRIPT —{fact_path, operator, value}reusing the dynamic-group condition evaluator (same operators, samedevice.inventory.*field resolution), feeding the existing compliance statuses/grace periodsdays_until_expiryfrom the stored PEM; assigned-action-converged from execution status); T2 inventory extension via the existing baseline collector (firewall active + default policies, pending security updates — SDK managers already read this state)Triaged out (decided 2026-07-03)
-m everywhereis the v1 shape when it comes back.Cross-cutting requirements (apply to every action)
@gotags validate.sys/*packages; backend selected at construction, never per call.Related