MidnightBSD merged a complete age verification subsystem on March 9, 2026 (PR #302), with additional components through March 26. It is the first operating system to ship a native age reporting implementation. The system consists of a daemon, a CLI tool, a C library, and a set of Unix groups for package-level enforcement.
This is not a proposal, not a PR under review, not a mailing list thread. MidnightBSD has merged and shipped a working age verification daemon. It is enabled by default.
The aged subsystem takes a fundamentally different approach from the Linux ecosystem's D-Bus/portal/systemd stack. There is no D-Bus. There is no systemd. There are no portals. This is a traditional Unix daemon with a Unix domain socket, a C library in libutil, and group-based access control — the BSD way.
A privileged daemon that manages age data for all users on the system. Stores records in a
SQLite database at /var/db/aged/aged.db. Listens on a Unix domain socket at
/var/run/aged/aged.sock. Starts before LOGIN in the boot sequence. Drops
privileges to a dedicated aged user (UID 866) after binding the socket.
Enabled by default: aged_enable="YES" is set in rc.conf.
Command-line interface for interacting with the daemon. Any user can query their own age bracket. Root can set age or date of birth for any user and manage age group memberships.
Three C functions added to libutil, available to any application compiled against MidnightBSD:
agev_get_age_bracket(), agev_set_age(), and
agev_set_dob(). This is the application-facing API — the equivalent of
what the Linux ecosystem is trying to build with xdg-desktop-portal.
Introduced in PR #310.
Four Unix groups for age-based access control, used by MidnightBSD's mport package manager to gate package installation:
age4p (GID 856) — users aged 4 and olderage13p (GID 857) — users aged 13 and olderage16p (GID 858) — users aged 16 and olderage18p (GID 859) — users aged 18 and olderIntroduced in PR #317 (March 26, 2026). That PR is flagged "AI-Assisted-by: gemini 2.5 pro".
The Linux age verification ecosystem is building a multi-layer stack: systemd stores the data, xdg-desktop-portal exposes the API, and desktop environments implement backends. The MidnightBSD approach is architecturally simpler and, by BSD standards, more conventional.
The practical consequence: on Linux, age reporting is still forming across multiple upstream projects and no distribution has shipped an integrated mechanism. On MidnightBSD, the entire stack — storage, API, enforcement — shipped in a single merge.
The daemon listens on /var/run/aged/aged.sock. The protocol is plaintext over
a Unix domain socket. Behavior depends on the caller's privilege level.
Root sends structured commands:
SET <uid> <type> <value>
Where type is either age or dob, and
value is the integer age (2–125) or date of birth string.
Any data sent by a non-root client triggers a lookup of the caller's own UID. The daemon responds with the caller's age bracket. The content of the message is irrelevant — sending anything triggers the lookup.
The daemon responds with a comma-delimited pair representing the lower and upper bounds of the age bracket:
| Response | Meaning |
|---|---|
0,12 |
Under 13 |
13,15 |
13 – 15 |
16,17 |
16 – 17 |
18,-1 |
18+ (no upper bound) |
-1,-1 |
Unknown (no age data provided) |
The four age brackets correspond to the categories required by AB 1043:
0,12
Under 13
Group: age4p
13,15
13 – 15
Group: age13p
16,17
16 – 17
Group: age16p
18,-1
18+
Group: age18p
Users with UIDs below 1000 (service accounts, root, daemon, etc.)
default to the 18+ bracket if no age has been explicitly set. Age values must be between 2
and 125 inclusive.
Three functions in libutil, declared in <libutil.h>:
int agev_get_age_bracket(uid_t uid, int *lower, int *upper);
Connects to the aged socket, sends a lookup request for the given UID, and parses the
comma-delimited response into lower and upper bounds.
int agev_set_age(uid_t uid, int age);
Sends SET <uid> age <age> to the daemon. Requires root.
int agev_set_dob(uid_t uid, const char *dob);
Sends SET <uid> dob <dob> to the daemon. Requires root.
Because these functions are in libutil, any application compiled against MidnightBSD's base system can query age brackets with a single function call. No library to install, no service to discover, no IPC mechanism to negotiate. This is the simplest possible application-facing API.
PR #317 added four Unix groups that the aged daemon manages based on the user's age bracket. The mport package manager checks group membership to gate package installation:
When a user's age is set via agectl or the agev(3) library, the daemon
calculates the appropriate bracket and adds the user to the corresponding groups. A
16-year-old would be added to age4p, age13p, and
age16p but not age18p.
Packages in the mport repository can declare a minimum age group. A package marked as
requiring age18p will not install for users who are not members of that group.
This is enforcement at the package manager level — not just data collection. The Linux ecosystem has not proposed anything equivalent. The D-Bus proposal and xdg-desktop-portal PR define query APIs; they do not restrict what software a user can install based on their reported age.
agev_get_age_bracket() (introduced in
PR #310)
splits the daemon's response on '-' instead of ','. The daemon
returns comma-delimited responses (e.g., "13,15"), but the library function
calls strtok(response, "-").
This means 4 of the 5 possible response types are parsed incorrectly:
"0,12" — never split, parsed as a single token"13,15" — never split, parsed as a single token"16,17" — never split, parsed as a single token"18,-1" — split on the - in -1, producing "18," and "1""-1,-1" — split on the first -, producing "" and "1," and "1"
The only response that would partially survive is "18,-1", and even that
produces an incorrect upper bound of 1 instead of -1.
The aged daemon is purely self-declared. No identity verification, no document checks, no network calls. The user (or root on their behalf) states an age, and the system believes them.
The PR author acknowledged this directly:
"This will not comply with Brazil or the proposed law in New York. (they require ID checks)" — PR #302
This satisfies AB 1043's self-declaration model but is insufficient for Brazil Lei 15.211 (which bans self-declaration) and NY S8102A (which requires "commercially reasonable" verification).
PR #302 received two public comments, both negative:
MidnightBSD's aged daemon is the first age reporting implementation to ship. It is also the
first for which Ageless Linux provides non-speculative removal instructions. The daemon can
be disabled via rc.conf, the database deleted, and age group memberships removed.
Note: become-ageless.sh targets Debian-based Linux systems and does not support
BSD. The MidnightBSD removal procedure is documented as manual steps.