From b0c5452e3bb1e3bdbb27181e331fc959fa43f4fc Mon Sep 17 00:00:00 2001 From: Tom van der Lee Date: Thu, 24 Sep 2026 15:28:24 +0000 Subject: Detect NixOS host timezone automatically instead of defaulting to UTC Enables services.automatic-timezoned so every host's clock/calendar reflects its actual location with no manual timedatectl step or auth prompt, since GNOME's own automatic-timezone toggle isn't wired to a passwordless polkit rule on NixOS the way KDE's is. Co-Authored-By: Claude Sonnet 5 --- .../.openspec.yaml | 2 ++ .../2026-09-24-add-automatic-timezone/design.md | 41 ++++++++++++++++++++++ .../2026-09-24-add-automatic-timezone/proposal.md | 26 ++++++++++++++ .../specs/automatic-timezone/spec.md | 23 ++++++++++++ .../2026-09-24-add-automatic-timezone/tasks.md | 10 ++++++ 5 files changed, 102 insertions(+) create mode 100644 openspec/changes/archive/2026-09-24-add-automatic-timezone/.openspec.yaml create mode 100644 openspec/changes/archive/2026-09-24-add-automatic-timezone/design.md create mode 100644 openspec/changes/archive/2026-09-24-add-automatic-timezone/proposal.md create mode 100644 openspec/changes/archive/2026-09-24-add-automatic-timezone/specs/automatic-timezone/spec.md create mode 100644 openspec/changes/archive/2026-09-24-add-automatic-timezone/tasks.md (limited to 'openspec/changes/archive') diff --git a/openspec/changes/archive/2026-09-24-add-automatic-timezone/.openspec.yaml b/openspec/changes/archive/2026-09-24-add-automatic-timezone/.openspec.yaml new file mode 100644 index 0000000..75289e4 --- /dev/null +++ b/openspec/changes/archive/2026-09-24-add-automatic-timezone/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-09-24 diff --git a/openspec/changes/archive/2026-09-24-add-automatic-timezone/design.md b/openspec/changes/archive/2026-09-24-add-automatic-timezone/design.md new file mode 100644 index 0000000..0037bd5 --- /dev/null +++ b/openspec/changes/archive/2026-09-24-add-automatic-timezone/design.md @@ -0,0 +1,41 @@ +## Context + +See `proposal.md` - Why. Current state: `modules/nixos/common.nix` sets `time.timeZone = lib.mkDefault "Etc/UTC";` and nothing ever updates it. Relevant constraints discovered while investigating the pinned `nixos-unstable` nixpkgs (`nixos/modules/services/system/automatic-timezoned.nix`, `nixos/modules/services/desktops/geoclue2.nix`, `nixos/modules/services/desktop-managers/plasma6.nix`, and `systemd`'s shipped `org.freedesktop.timedate1.policy`): + +- `systemd`'s own default polkit policy requires `auth_admin` for `org.freedesktop.timedate1.set-timezone` for any subject, active or not. Nothing in nixpkgs relaxes that for GNOME. +- The KDE Plasma 6 module (`plasma6.nix`) adds its own polkit rule allowing an *active* session to call `set-timezone` without a prompt, backing Plasma's own automatic-timezone UI toggle. +- nixpkgs' GNOME module (`nixos/modules/services/desktops/gnome`) adds no equivalent rule. GNOME Settings' own "Automatic Time Zone" toggle (backed by `gnome-settings-daemon`'s datetime plugin) would therefore hit the default `auth_admin` prompt on this flake's NixOS hosts today - it isn't wired to a passwordless mechanism the way Plasma's is. +- `services.automatic-timezoned` (a small dedicated daemon) is nixpkgs' own DE-agnostic answer to this gap: when enabled it sets `time.timeZone = null;` itself, turns on `services.geoclue2` with its own `appConfig` entry, and ships a polkit rule scoped to its own dedicated `automatic-timezoned` system user - so it runs unattended regardless of desktop environment or which user is logged in. + +## Goals / Non-Goals + +**Goals:** +- Every NixOS host's `time.timeZone` reflects actual physical location shortly after boot, with no manual step and no interactive authentication. +- The mechanism works the same regardless of which user (if any) is logged into the GNOME session, since it's a system service, not a per-session one. + +**Non-Goals:** +- Making GNOME Settings' own "Automatic Time Zone" toggle functional. It's a separate, unwired mechanism on NixOS (see Context) and fixing that is out of scope - `automatic-timezoned` achieves the actual user-visible goal (correct system timezone, which GNOME's clock/calendar read directly from `time.timeZone`/`/etc/localtime`) without it. +- Manual/static location configuration (`services.geoclue2.enableStatic` + `location.latitude`/`longitude`). Network-based geolocation is sufficient for a desktop machine that's expected to have network access shortly after boot. +- Any change to `homeConfigurations` (standalone home-manager, no NixOS layer) - they have no `time.timeZone` to manage. + +## Decisions + +**Use `services.automatic-timezoned.enable = true;` in `modules/nixos/common.nix`, rather than trying to make GNOME's own toggle work.** + +Alternatives considered: +1. **Add a NixOS polkit rule scoped to active GNOME sessions**, mirroring `plasma6.nix`'s `subject.active` rule, then rely on GNOME's own `gsd-datetime` plugin (toggled on via a `dconf` default, the same pattern already used for dark mode in `common.nix`). Rejected: this reverse-engineers and depends on `gsd-datetime`'s internal behavior, which isn't a stable, documented NixOS integration point the way `automatic-timezoned` is: it's exactly the gap `automatic-timezoned` exists to fill for GNOME. It would also only run while a graphical session is active, whereas `automatic-timezoned` is a system service independent of any login. +2. **`services.automatic-timezoned.enable = true;`** (chosen): purpose-built for this exact scenario, self-contained (enables and configures its own `geoclue2` + polkit rule, no manual wiring), maintained in nixpkgs itself, and already proven for the same problem this repo solved for Docker (`container-runtime`) - prefer an existing, documented NixOS module over recreating its internals. + +**Leave `time.timeZone = lib.mkDefault "Etc/UTC";` in place rather than removing it.** + +`automatic-timezoned`'s module sets `time.timeZone = null;` at normal priority when enabled, which unconditionally overrides the existing `mkDefault`. Leaving the line in place costs nothing and keeps `Etc/UTC` as the documented, explicit fallback if `automatic-timezoned` is ever disabled again on a specific host, without needing to touch `common.nix` a second time. + +## Risks / Trade-offs + +- **[Risk]** A host with no network access at boot (or geoclue2's beacondb.net lookup failing) never gets a timezone update, and stays on whatever was last applied (or `Etc/UTC` on first boot). → **Mitigation**: none needed for this repo's target hardware (desktops/laptops expected to have network shortly after boot); already called out as a known limitation in `proposal.md`. +- **[Risk]** GNOME's own "Automatic Time Zone" toggle in Settings remains visually present but functionally disconnected from what's actually keeping the timezone correct, which could read as broken/confusing if a user flips it expecting it to be the mechanism. → **Mitigation**: documented as a known limitation in `proposal.md`; the actual observable goal (correct time shown everywhere) is met regardless of that toggle's state, and no further UI work is planned here. +- **[Risk]** A future host that legitimately needs a fixed, non-default timezone must remember to set it at higher-than-default priority (`lib.mkForce` or a plain assignment beats `automatic-timezoned`'s normal-priority `null`, but a naive `lib.mkDefault` override would silently lose to it). → **Mitigation**: called out explicitly in `proposal.md`'s BREAKING/Impact notes; the module's own evaluation-time error (if a plain/default-priority conflicting value is set) makes a silent miss unlikely. + +## Migration Plan + +Additive, single-file change (`modules/nixos/common.nix`) applied to every NixOS host on next `nixos-rebuild switch` (or equivalent activation). No data migration. Rollback is reverting the one-line addition and rebuilding; `time.timeZone` immediately falls back to the existing `Etc/UTC` default with no other state to clean up (the `automatic-timezoned`/`geoclue` system user and services are removed by NixOS's normal generation switch). diff --git a/openspec/changes/archive/2026-09-24-add-automatic-timezone/proposal.md b/openspec/changes/archive/2026-09-24-add-automatic-timezone/proposal.md new file mode 100644 index 0000000..6c2c77c --- /dev/null +++ b/openspec/changes/archive/2026-09-24-add-automatic-timezone/proposal.md @@ -0,0 +1,26 @@ +## Why + +Every NixOS host built from this flake currently defaults to `Etc/UTC` (`modules/nixos/common.nix`) and nothing ever changes that, so a freshly booted or freshly relocated machine shows the wrong local time/date everywhere (GNOME's clock, calendar, screenshots, log timestamps) until someone manually runs `timedatectl set-timezone`. Tom wants the timezone detected automatically instead. + +## What Changes + +- Enable `services.automatic-timezoned` unconditionally on every NixOS host, in `modules/nixos/common.nix`, alongside the other unconditional host-level services (GNOME, SSH, printing). This is the NixOS-native daemon for this purpose: it uses `geoclue2` (which it enables and configures itself) to determine location and `systemd-timedated` to actually apply the timezone, and it ships its own polkit rule so the update happens automatically with no interactive auth prompt. +- The existing `time.timeZone = lib.mkDefault "Etc/UTC";` in `modules/nixos/common.nix` is left as-is: `automatic-timezoned` overrides it internally (`time.timeZone = null;`, at normal priority) whenever it's enabled, so `Etc/UTC` only remains as the boot-time value until the daemon determines and applies the real one shortly after boot (network permitting). No host in this repo (checked `hosts/test-vm`, the only real host, and `hosts/template`) sets `time.timeZone` explicitly, so there's no conflict with the module's requirement that an explicit timezone be set with a priority lower than default. +- **Not** relying on GNOME's own Settings → Date & Time → "Automatic Time Zone" toggle: NixOS's `systemd` package ships `org.freedesktop.timedate1.set-timezone` requiring `auth_admin` by default, and unlike the KDE Plasma module (`nixos/modules/services/desktop-managers/plasma6.nix`), nixpkgs' GNOME module adds no polkit rule to relax that for GNOME's own timezone daemon. `automatic-timezoned` ships and enables exactly that rule for its own dedicated service user, so it works without depending on GNOME-side authorization plumbing that isn't wired up on NixOS today. See `design.md` for the alternatives considered here. +- **BREAKING**: none — additive only. A host that later needs a fixed non-`Etc/UTC` timezone can still set `time.timeZone` at a priority higher than default (`lib.mkForce` or a plain assignment) in its own `configuration.nix`; doing so alongside this change without adjusting priority would now raise the same explicit "you set a timezone with `automatic-timezoned` enabled" evaluation error the upstream module already raises by design, which is the intended, deliberate-override behavior — not a silent conflict. + +## Capabilities + +### New Capabilities +- `automatic-timezone`: every NixOS host built from this flake keeps its system timezone (and therefore the desktop's clock/calendar) in sync with the host's actual location automatically, without manual `timedatectl` intervention or an interactive authorization prompt. + +### Modified Capabilities +(none) + +## Impact + +- **Affected code**: `modules/nixos/common.nix` (add `services.automatic-timezoned.enable = true;`). +- **Not affected**: `modules/home/*` (home-manager-only `homeConfigurations` have no NixOS-level timezone service and are unaffected — they already inherit whatever timezone the host OS provides), `hosts/*/configuration.nix` (no host currently overrides `time.timeZone`), `lib/unfree-allowlist.nix` (`automatic-timezoned` is GPL-3.0, free). +- **Dependencies**: nixpkgs' `automatic-timezoned` and `geoclue2` packages, both already present in the pinned `nixos-unstable` input; no new flake inputs. `geoclue2`'s default network-based location source (`api.beacondb.net`) requires outbound internet access, which this repo's firewall config (inbound-only restrictions) doesn't block. +- **Platforms**: NixOS hosts only (`modules/nixos/common.nix`), via any host built by `lib/mkNixos.nix`. No effect on `homeConfigurations` (`tom@linux`, `tom@darwin`). +- **Known limitation, out of scope**: GNOME's own "Automatic Time Zone" toggle in Settings → Date & Time is not wired to this mechanism and may still prompt for authentication if the user flips it manually; the actual timezone is kept correct by the independent `automatic-timezoned` service regardless of that toggle's state. No reverse-geocoding/location-display UI is added — this only keeps `time.timeZone` current. Timezone detection is network-dependent (Wi-Fi/IP-based geolocation via `geoclue2`); a host with no network at boot keeps whatever timezone was last applied (or `Etc/UTC` on first boot) until connectivity returns. diff --git a/openspec/changes/archive/2026-09-24-add-automatic-timezone/specs/automatic-timezone/spec.md b/openspec/changes/archive/2026-09-24-add-automatic-timezone/specs/automatic-timezone/spec.md new file mode 100644 index 0000000..c9c50bf --- /dev/null +++ b/openspec/changes/archive/2026-09-24-add-automatic-timezone/specs/automatic-timezone/spec.md @@ -0,0 +1,23 @@ +## Purpose + +Keeps every NixOS host's system timezone in sync with its actual physical location automatically, so the system clock, GNOME's clock/calendar, and log timestamps reflect local time without any manual `timedatectl` step. + +## ADDED Requirements + +### Requirement: NixOS hosts detect and apply their timezone automatically +Every NixOS host built from this flake's shared system module SHALL determine its current timezone based on location and apply it to the system, without requiring the user to run a manual command or interactively authenticate the change. + +#### Scenario: Timezone is applied after boot with network access +- **WHEN** a NixOS host built from this flake's shared system module boots with network connectivity available +- **THEN** the system's active timezone is set to the timezone matching the host's current location, without any interactive prompt + +#### Scenario: No per-host opt-in required +- **WHEN** a NixOS host is built from this flake's shared system module and its own `hosts//configuration.nix` does not configure timezone detection itself +- **THEN** automatic timezone detection is still active, exactly as if it had been configured directly + +### Requirement: A host can still pin an explicit fixed timezone +A NixOS host built from this flake SHALL be able to override automatic detection with a fixed, explicitly configured timezone when that host's own configuration requires one. + +#### Scenario: Host-level override takes effect +- **WHEN** a host's own `hosts//configuration.nix` sets an explicit timezone at a priority higher than the shared default +- **THEN** that host uses the explicitly configured timezone instead of an automatically detected one diff --git a/openspec/changes/archive/2026-09-24-add-automatic-timezone/tasks.md b/openspec/changes/archive/2026-09-24-add-automatic-timezone/tasks.md new file mode 100644 index 0000000..e139bbd --- /dev/null +++ b/openspec/changes/archive/2026-09-24-add-automatic-timezone/tasks.md @@ -0,0 +1,10 @@ +## 1. Enable automatic timezone detection + +- [x] 1.1 Add `services.automatic-timezoned.enable = true;` to `modules/nixos/common.nix`, alongside the other unconditional host-level services +- [x] 1.2 Verify `nix eval '.#nixosConfigurations.test-vm.config.services.automatic-timezoned.enable'` (or the appropriate real host output) evaluates to `true`, and that `.config.services.geoclue2.enable` and `.config.time.timeZone` evaluate to `true` and `null` respectively (confirming `automatic-timezoned` took over from the `Etc/UTC` default as expected) — this repo has no host tracked in git today (`hosts/test-vm` is a local-only scratch VM excluded via `.git/info/exclude`, so it's invisible to the flake's git-tree evaluation and `nixosConfigurations` is currently empty); verified instead via a synthetic `nixpkgs.lib.nixosSystem` built directly from `modules/nixos/common.nix`, confirming `services.automatic-timezoned.enable = true`, `services.geoclue2.enable = true`, and `time.timeZone = null` + +## 2. Verification + +- [x] 2.1 Run `nix flake check` and confirm it passes +- [x] 2.2 Run `nix build '.#nixosConfigurations.test-vm.config.system.build.toplevel'` (or the appropriate real host output) and confirm it succeeds — same test-vm caveat as 1.2; built the synthetic `nixosSystem`'s `config.system.build.toplevel` instead, which succeeded natively (this machine is aarch64-linux) and produced a closure including the `automatic-timezoned`, `automatic-timezoned-geoclue-agent`, `geoclue`, and `systemd-timedated` units +- [x] 2.3 Run `nix fmt` and confirm `modules/nixos/common.nix` is formatted with no diff -- cgit v1.3