Getting started

From a downloaded binary to a goal event you can see, without waiting for a real game. About five minutes, no Home Assistant instance required for this part.

Before you start

You need a terminal and a Goalfeed binary for your platform. This tutorial uses the test-goals option, which fires a synthetic event once a minute through the exact same code path a real goal uses — so the "success" at the end of this page is the real pipeline, not a mockup. Full install options (Home Assistant add-on, Docker, source) are in the install guide; this page uses the plain binary because it needs no add-on store and no Home Assistant instance.

1. Get a binary (about 1 minute)

Download the archive for your platform from the latest release and extract it. On Linux/macOS with the GitHub CLI installed:

bash
# swap linux_amd64 for darwin_arm64, windows_amd64, etc. — see Install for the full list
gh release download --repo goalfeed/goalfeed --pattern "*linux_amd64.tar.gz"
tar xzf goalfeed_*_linux_amd64.tar.gz
cd goalfeed_*_linux_amd64 2>/dev/null || true

Expected result: a goalfeed binary (plus a web/frontend/build directory bundled in the archive) in your current directory.

2. Write a minimal config.yaml (about 1 minute)

In the same directory as the binary, create config.yaml:

config.yaml
watch:
  nhl:
    - WPG
test-goals: true
web: true
web-port: "8099"

No Home Assistant URL or token yet — that's deliberate. This step is about proving the loop fires, not about wiring the goal horn (that's the next page). There's no --config flag; Goalfeed only ever looks for ./config.yaml in the working directory it's started from — see the configuration reference.

3. Run it (a few seconds)

bash
./goalfeed

Expected output (real, captured — Goalfeed logs structured JSON via zap, one line per event):

console
{"level":"info","ts":1787264681.604558,"caller":"goalfeed/main.go:171","msg":"Puck Drop! Initializing Goalfeed Process"}
{"level":"info","ts":1787264681.604706,"caller":"goalfeed/main.go:181","msg":"Initializing Active Games"}
{"level":"info","ts":1787264681.604725,"caller":"goalfeed/main.go:192","msg":"Updating Active Games"}
{"level":"info","ts":1787264681.604807,"caller":"goalfeed/main.go:199","msg":"Checking for active NHL games"}
…
{"level":"info","ts":1787264681.6063309,"caller":"goalfeed/main.go:161","msg":"Starting Goalfeed in web mode"}
…
[GIN-debug] Listening and serving HTTP on :8099

Leave this running — it keeps polling in the foreground. Open a second terminal for the next step.

4. Watch for the test goal (up to 1 minute)

test-goals fires on a one-minute ticker (time.NewTicker(1 * time.Minute) in main.go), so the first one lands somewhere between 0 and 60 seconds after startup — there's no way to make it fire sooner, and no reason to make it fire slower. Poll the app log through the REST API:

bash
curl -s "http://localhost:8099/api/logs?limit=1"

Expected output (real, captured from the run above — reformatted for readability):

json
{
  "id": "1787264741605724000-TEST-",
  "type": "event",
  "teamCode": "TEST",
  "target": "ha:event:goal",
  "success": false,
  "error": "refusing to send Home Assistant request: home assistant url is empty",
  "timestamp": "2026-08-20T17:25:41.605724-05:00"
}

That "success": false is expected and correct — there's no Home Assistant configured yet, so Goalfeed refused to send the request rather than silently drop it (see Security for why). What it proves is the whole pipeline: config loaded, the ticker fired on schedule, the event was built, and it's queryable a second later over the REST API — the same path a real NHL goal takes. If the bundled web UI built (step 1's release archive includes it), you can watch the same thing happen visually: open http://localhost:8099, click the Logs tab, and the entry appears there within the same minute, live over the WebSocket feed, with no page refresh.

This is the guaranteed part

This step doesn't depend on a real game being in progress, on any specific league being in season, or on Home Assistant being reachable. It only depends on the one-minute ticker, which always fires. That's why this tutorial uses test-goals instead of "go watch for a real goal" — the latter could mean waiting hours.

What's next

You've proven Goalfeed runs, polls, and logs an event end to end. From here:

  • Point it at a real Home Assistant instance and get the goal horn and lights firing — Home Assistant automations.
  • See every install channel (add-on, source, Docker's current caveats) — Install.
  • Look up every config key, including the ones this page skipped (Home Assistant URL/token, other leagues) — Configuration reference.
  • Something not behaving as shown above? — Troubleshooting.