Skip to content

Report a startup event to trace, off the main thread and with an opt-out - #22

Merged
dmccoystephenson merged 1 commit into
mainfrom
usage-reporting-via-trace
Sep 12, 2026
Merged

dmccoystephenson merged 1 commit into
mainfrom
usage-reporting-via-trace

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

gh-backup is wired into the maintainers' trace usage-reporting service. trace-client-java is vendored unmodified apart from its package line as com.github.backup.trace.TraceClient (with its tests), and a new UsageReportingService bean builds the client from gh-backup's existing Spring properties, sends the events, and closes the client when the context shuts down.

Closes #21

Guarantees

Reporting is done from a single daemon thread owned by the client: report() returns immediately, never throws, and queues at most 256 events before dropping new ones, so a trace server that is down, slow or rejecting the key costs a dropped report and nothing else. A blank endpoint, an unwritable home directory or an empty USAGE_REPORTING_ENABLED are all handled without an exception reaching a backup. On shutdown, Spring Boot's shutdown hook calls close(), which waits at most the client's 5-second read timeout for an event in flight, so a short CLI run does not exit before its startup event has left the machine, and a hung server cannot hold exit for longer than that.

What is sent

  • startup, once per process, tagged version = the program version (spring.application.version, filled in by Maven resource filtering from the POM; if the placeholder is ever left unfiltered, the tag is omitted rather than sent as @project.version@)
  • backup-completed, when a backup run finishes, with no tags: the end of a CLI run, an interactive backup command, a scheduled daemon run, and a web POST /api/backups

What is not sent

No user or organization names, no repository names, no counts, no paths, no usernames, hostnames or IP addresses. The body of a startup event is exactly {"application":"gh-backup","name":"startup","tags":{"version":"2.0.0-SNAPSHOT-8-8-2026"}}.

Where the opt-out is

usage.reporting.enabled=false, through the same mechanism as every other gh-backup property: -Dusage.reporting.enabled=false, an application.properties next to the JAR, or USAGE_REPORTING_ENABLED=false in the environment (which docker-compose.yml now passes through from .env, defaulting to true). usage.reporting.endpoint and usage.reporting.key are documented alongside it in CONFIG.md.

The first time reporting runs on a machine, one INFO line is logged: Usage reporting is on: gh-backup sends a startup event (program name and version only) and a backup-completed event (nothing else) to trace.danielstephenson.dev. Turn it off with -Dusage.reporting.enabled=false or USAGE_REPORTING_ENABLED=false. A marker file at ~/.config/gh-backup/usage-reporting-notice-shown keeps it from being repeated. gh-backup has no settings file of its own to record this in, which is why a marker file under the user's config directory is used. When reporting is off, nothing is logged and no marker is written.

Test plan

  • mvn clean verify on JDK 17 (CI's toolchain): 54 tests (1 skipped, pre-existing) before → 72 tests (1 skipped) after, BUILD SUCCESS. The 18 new tests are the 10 vendored TraceClientTest cases and 8 UsageReportingServiceTest cases driving the wiring against a loopback JDK HttpServer: startup body and bearer key, backup-completed body, unfiltered-version fallback, notice logged once and not on a second run, disabled sends nothing and writes no marker, blank/absent enabled means on, bad endpoint and unwritable marker never throw.
  • sh docker-entrypoint-test.sh: All docker-entrypoint.sh tests passed.
  • unzip -l target/gh-backup-2.0.0-SNAPSHOT-8-8-2026.jar lists BOOT-INF/classes/com/github/backup/trace/TraceClient.class; the bundled application.properties inside the JAR carries spring.application.version=2.0.0-SNAPSHOT-8-8-2026 and the key byte-for-byte.
  • Every @SpringBootTest context sets usage.reporting.enabled=false, and surefire sets the same system property for any test added later, so no test ever reaches the real service.

Local stub proof (a 12-line Python http.server recording POSTs, -Duser.home pointed at an empty directory, -Dusage.reporting.endpoint at the stub; production was never contacted):

run 1  java -jar ... (no args)   → notice logged once; stub received
       /api/metrics auth=ok body={"application":"gh-backup","name":"startup","tags":{"version":"2.0.0-SNAPSHOT-8-8-2026"}}
run 2  same                      → no notice; startup body received again
run 3  USAGE_REPORTING_ENABLED=false, fresh home → nothing received, no notice, no marker written
run 4  USAGE_REPORTING_ENABLED= (empty) → treated as on, no exception
run 5  java -jar ... <nonexistent user> → startup body, then
       /api/metrics auth=ok body={"application":"gh-backup","name":"backup-completed"}
run 6  stub sleeps 60 s before answering → process exits after 6.98 s (≈2 s baseline + 5 s read timeout)
run 7  endpoint on a closed port → exits in 1.92 s, same as reporting off (1.97 s)

🤖 Generated with Claude Code

https://claude.ai/code/session_01WYoD9SsaRz8PjakTSHhmn6


drafted by Claude on behalf of Daniel Stephenson

A startup event (program name and version only) and a backup-completed
event (nothing else) are sent to the trace service through the vendored
trace-client-java, from a daemon thread that never delays a backup and
never holds up exit by more than the client's 5-second timeout. Nothing
about the users, organizations or repositories being backed up is sent.

Reporting is on by default and is turned off with
usage.reporting.enabled=false (a -D property, application.properties, or
USAGE_REPORTING_ENABLED in the environment, which docker-compose.yml now
passes through). A one-line notice is logged the first time it runs on a
machine and recorded in ~/.config/gh-backup/ so it is not repeated;
gh-backup has no settings file of its own, which is why a marker is used.

The program version comes from spring.application.version, filled in by
Maven resource filtering from the POM. Every test that starts a Spring
context runs with reporting off, and surefire pins the same for any test
added later, so nothing here ever reaches the real service from a build.

Closes #21

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WYoD9SsaRz8PjakTSHhmn6
@dmccoystephenson
dmccoystephenson merged commit 97f6f65 into main Sep 12, 2026
2 checks passed
@dmccoystephenson
dmccoystephenson deleted the usage-reporting-via-trace branch September 12, 2026 05:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Report a startup event to trace, off the main thread and with an opt-out

1 participant