Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,9 @@ jobs:
- name: Run bats (bonedigger report)
run: bats tests/test_bonedigger_report.bats

- name: Run bats (projectbluefin-countme)
run: bats tests/test_projectbluefin_countme.bats

- name: Run bats (apps.just — install-opentabletdriver, cncf)
run: bats tests/test_apps_just.bats
- name: Run bats (escl-fixture)
Expand All @@ -173,9 +176,6 @@ jobs:
- name: Run bats (bling preexec re-arm)
run: bats tests/test_bling_preexec_rearm.bats

- name: Run bats (bluefin-countme)
run: bats tests/test_bluefin_countme.bats

- name: Run bats (brew tap trust)
run: bats tests/test_brew_tap_trust.bats

Expand Down
3 changes: 1 addition & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ test:
bats tests/test_privileged_setup.bats
bats tests/test_bling.bats
bats tests/test_bling_sh.bats
bats tests/test_bluefin_countme.bats
bats tests/test_bling_preexec_rearm.bats
bats tests/test_luks_tpm2.bats
bats tests/test_rechunker_group_fix.bats
Expand Down Expand Up @@ -46,8 +45,8 @@ test:
bats tests/test_damask_service.bats
bats tests/test_suite_registration.bats
bats tests/test_shared_just.bats
bats tests/test_bluefin_countme.bats
bats tests/test_escl_fixture.bats
bats tests/test_projectbluefin_countme.bats

# Preview Bazaar config from this checkout on the local machine
bazaar-preview:
Expand Down
47 changes: 27 additions & 20 deletions docs/skills/image-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,26 +101,33 @@ IMAGE_REGISTRY="ghcr.io/${IMAGE_VENDOR}"

The Containerfile pulls wallpaper artwork from `ghcr.io/ublue-os/bluefin-wallpapers-gnome` as a **build-time COPY source**. This is a read-only upstream artwork dependency and does not violate the ublue-os prohibition. The production image tree and all runtime registries are fully under `ghcr.io/projectbluefin/`. See [`containerfile/SKILL.md`](containerfile/SKILL.md) for details.

## Dakota CountMe reporting

Only Dakota currently publishes a first-party count. It ships
`/usr/libexec/bluefin-countme` and `bluefin-countme.{service,timer}` from this
repo through Dakota's pinned `elements/bluefin/common.bst`. The units require
`ID=bluefin-dakota`; the script also exits before reporting from other images.

The timer tries on activation and once per calendar day, so an offline attempt
can retry. The script records a success in `/var/lib/bluefin-countme/lastrun`
only after `/metalink` accepts the ping and sends at most once per UTC
Monday-anchored week. It reads `:stable` or `:testing` from the booted image
ref, including tagged digest refs. The baked `latest` tag is not a stream: if
the booted ref provides no valid stream, it retries instead of reporting it.
The opt-out is `systemctl mask --now bluefin-countme.timer bluefin-countme.service`.

`countme.projectbluefin.io/counts.json` derives Dakota's named stream counts
from its own D1 rows; other tags remain unclassified. These are estimated
check-ins without a machine identifier, not a distinct-device census. Fedora
countme and `ublue-os/countme` are not sources for Project Bluefin counts;
Bluefin Classic's upstream chart is separate.
## CountMe reporting (Dakota and Utah)

`/usr/libexec/projectbluefin-countme` and `projectbluefin-countme.{service,timer}`
ship from `system_files/shared` to every consumer; Dakota gets them through
its pinned `elements/bluefin/common.bst`, Utah through `COMMON_IMAGE_SHA`. The
script exits before any network call unless `image-info.json`'s `image-name`
starts with `dakota` or `utah`, so Bluefin Classic and LTS never report.

The timer and service follow upstream
[`eos-phone-home`](https://github.com/endlessm/eos-phone-home): run on
activation and three hours after each run, skip machines with an empty
`/home`. The script sends at most once per 24h (`/var/lib/projectbluefin-countme/last`),
only `{"image": "<image-name>/<image-flavor>:<stream>"}` to
`PUT countme.projectbluefin.io/v1/ping`. The stream is the booted ref's tag
from `bootc status` (`stable`, `testing`, else `unknown`), never the baked
`image-tag`. No booted bootc image means no report. A failed send exits 0 and
the next timer run retries.

Opt out with `systemctl mask --now projectbluefin-countme.timer`. The previous
opt-outs still block the service: the files `/etc/projectbluefin/countme/disabled`,
`/etc/bluefin-countme-opt-out`, `/etc/dakota-countme/disabled`, and a
`systemctl mask` of `bluefin-countme.timer` or `bluefin-countme.service`.

`countme.projectbluefin.io/v1/daily.json` lists systems active per UTC day
and image. These are anonymous check-ins without a machine identifier, not a
device census. Fedora countme and `ublue-os/countme` are not sources for
Project Bluefin counts.

## Runtime repository selection

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
enable projectbluefin-countme.timer
24 changes: 0 additions & 24 deletions system_files/shared/usr/lib/systemd/system/bluefin-countme.service

This file was deleted.

18 changes: 0 additions & 18 deletions system_files/shared/usr/lib/systemd/system/bluefin-countme.timer

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[Unit]
Description=Send daily active-system ping to Project Bluefin
Documentation=https://docs.projectbluefin.io/analytics
Wants=network-online.target
After=network-online.target
ConditionPathExists=/run/ostree-booted
ConditionDirectoryNotEmpty=/home
# Opt-out files honored by the previous bluefin-countme units.
ConditionPathExists=!/etc/projectbluefin/countme/disabled
ConditionPathExists=!/etc/bluefin-countme-opt-out
ConditionPathExists=!/etc/dakota-countme/disabled
# `systemctl mask` of the previous units, the opt-out documented for them.
ConditionPathExists=!/etc/systemd/system/bluefin-countme.timer
ConditionPathExists=!/etc/systemd/system/bluefin-countme.service

[Service]
Type=simple
ExecStart=/usr/libexec/projectbluefin-countme
StateDirectory=projectbluefin-countme

# Run as root (no DynamicUser) so bootc status can query status.
# Hardened sandbox:
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
NoNewPrivileges=yes
RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6
SystemCallFilter=@system-service @mount
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Unit]
Description=Send daily active-system ping to Project Bluefin

[Timer]
OnActiveSec=0
OnUnitInactiveSec=3h
AccuracySec=30m

[Install]
WantedBy=timers.target

This file was deleted.

149 changes: 0 additions & 149 deletions system_files/shared/usr/libexec/bluefin-countme

This file was deleted.

29 changes: 29 additions & 0 deletions system_files/shared/usr/libexec/projectbluefin-countme
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/bash
# /usr/libexec/projectbluefin-countme
# Anonymous daily active-system count for Dakota and Utah images. Sends only
# "<image-name>/<image-flavor>:<stream>", at most once per day, to
# countme.projectbluefin.io (docs.projectbluefin.io/analytics).
# Opt out: systemctl mask --now projectbluefin-countme.timer
set -uo pipefail

info="${IMAGE_INFO:-/usr/share/ublue-os/image-info.json}"
last="${STATE_DIRECTORY:-/var/lib/projectbluefin-countme}/last"

# Only images the service counts; everything else never touches the network.
name=$(jq -r '."image-name" // empty' "$info" 2>/dev/null)
[[ $name == dakota* || $name == utah* ]] || exit 0

[[ -e $last ]] && (( $(date +%s) - $(stat -c %Y "$last") < 86400 )) && exit 0

# No booted bootc image (live ISO, installer, container): nothing to count.
ref=$(bootc status --json 2>/dev/null | jq -r '.status.booted.image.image.image // empty' 2>/dev/null)
[[ -n $ref ]] || exit 0
ref=${ref%%@*}
tag=${ref##*:}
[[ $tag == stable || $tag == testing ]] || tag=unknown
image="$name/$(jq -r '."image-flavor" // "main"' "$info"):$tag"

# Like eos-phone-home, a failed send exits 0; the timer tries again later.
curl -fsS --max-time 30 -X PUT --json "$(jq -nc --arg i "$image" '{image: $i}')" \
"${COUNTME_HOST:-https://countme.projectbluefin.io}/v1/ping" >/dev/null && touch "$last"
exit 0
Loading
Loading