Skip to content
Closed
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
2 changes: 1 addition & 1 deletion .archcore/release/release-infrastructure.doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ The contract, the event properties, and the opt-out procedure are in install-scr
| `GITHUB_TOKEN` | this repo (automatic) | yes | Publishes the release. GitHub Actions provides it; nothing to configure. |
| `LANDING_DISPATCH_TOKEN` | this repo (secret) | no | A PAT with `contents: write` on `archcore-ai/landing`, used by `notify-landing.yml`. While it is absent, the job emits a warning and exits 0 — archcore.ai still picks the installer up on its next deploy, so a missing secret never turns a CLI push red. |
| `POSTHOG_KEY` | `archcore-ai/landing` (variable) | yes, for analytics | Public PostHog project key. Substituted into the installers at landing deploy time. A missing or non-`phc_` value fails the landing deploy. |
| `POSTHOG_HOST` | `archcore-ai/landing` (variable) | no | Ingestion host, `https://ph.archcore.ai`. Falls back to the same value when unset. |
| `POSTHOG_HOST` | `archcore-ai/landing` (variable) | no | Ingestion host, `https://edge.archcore.ai`. Falls back to the same value when unset. |

The pipeline needs no signing keys and no notarization credentials.

Expand Down
2 changes: 1 addition & 1 deletion .archcore/telemetry/cli-update-telemetry.spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Out of scope: the installer beacon's own events and payload, and every CLI event

- Command surface: `archcore update` and `archcore update --check` — `@cmd/update.go`. In this release, the unattended path carries no flag; its only caller is the MCP trigger.
- Sender: `internal/telemetry` [planned] — a package-level key variable, populated by `-X` ldflags in `@.goreleaser.yaml` at release, empty in every other build.
- Endpoint: `POST https://ph.archcore.ai/i/v0/e/`, PostHog capture payload — the endpoint `send_event()` in `@install.sh` already uses.
- Endpoint: `POST https://edge.archcore.ai/i/v0/e/`, PostHog capture payload — the endpoint `send_event()` in `@install.sh` already uses. Builds released before that change report to `https://ph.archcore.ai/i/v0/e/`, which is kept alive for them.
- Events: `cli_updated`, `cli_update_failed`, `cli_update_skipped`.
- Identifier: `${XDG_STATE_HOME:-$HOME/.local/state}/archcore/install-id`, the path `install_id_path()` in `@install.sh` writes and `updateCheckCachePath()` in `@cmd/update.go` mirrors.
- Stage categories on `cli_update_failed`, derived from the failure points of `@internal/update/update.go`: `check`, `download`, `checksum`, `extract`, `replace`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ An npm distribution channel was proposed first, on the theory that npm download

Measure installs in two independent ways, both reporting into the PostHog project that already serves archcore.ai.

1. **An installer beacon.** `install.sh` and `install.ps1` send one anonymous event per run — `cli_installed` on success, `cli_install_failed` with a `stage` category on failure — to `https://ph.archcore.ai/i/v0/e/`, the existing first-party ingestion proxy.
1. **An installer beacon.** `install.sh` and `install.ps1` send one anonymous event per run — `cli_installed` on success, `cli_install_failed` with a `stage` category on failure — to `https://edge.archcore.ai/i/v0/e/`, the first-party ingestion proxy.
2. **A release-counter bridge.** `.github/workflows/install-stats.yml` in `archcore-ai/landing` reports the GitHub `download_count` totals to PostHog on a daily schedule, sending both the denoised `checksums.txt` figure and the raw archive figure so the noise floor stays visible on the same chart.

The two measure different populations — the bridge counts asset fetches including automation, the beacon counts consenting machines — so each is the other's sanity check.
Expand Down Expand Up @@ -71,7 +71,7 @@ Honest, and useless for this metric — install-time opt-in rates are near zero.

### Alternative 4: An endpoint on archcore.ai that forwards to PostHog

Not available. archcore.ai is static GitHub Pages with no server, no edge functions, and no middleware, per the hosting ADR. The existing `ph.archcore.ai` proxy is the only first-party ingestion path.
Not available. archcore.ai is static GitHub Pages with no server, no edge functions, and no middleware, per the hosting ADR. The `edge.archcore.ai` proxy is the only first-party ingestion path.

## Consequences

Expand All @@ -85,7 +85,7 @@ Not available. archcore.ai is static GitHub Pages with no server, no edge functi
### Negative

- **A published privacy promise was reversed.** `/privacy` previously stated "No telemetry. We do not collect usage analytics, crash reports, identifiers, or any data from the plugin or CLI." The page now scopes that promise to the installed tools and documents the installer beacon separately. Any future change to what the beacon sends must update that copy in the same change.
- `ph.archcore.ai` resolves to Vercel, the platform the project migrated away from because it is unreliable in Russia. Beacons from affected users fail silently, so install geography is systematically skewed. The bridge is unaffected and partly compensates.
- The proxy is a single point of silent failure, and this was not hypothetical: `ph.archcore.ai` pointed at a Vercel deployment that the landing site's move to GitHub Pages deleted, and every beacon got a 404 for months without anything failing. It now runs on `edge.archcore.ai`, a PostHog managed reverse proxy behind Cloudflare, and CI proves the host reachable before a deploy ships against it. Cloudflare is more reachable from Russia than Vercel was, but not guaranteed, so install geography can still skew. The bridge is unaffected and partly compensates. See `analytics-host-must-reach-posthog.adr.md` in `archcore-ai/landing`.
- The beacon adds up to 3 s to an install on a network where the proxy is unreachable.
- The bridge's historical mode reports a per-release total *as of the day it runs*. The GitHub API exposes no historical series, so a true daily backfill is impossible; charted at publish dates it answers "which releases got picked up", not "installs per week".
- **A red or flaky `Install Smoke` run now blocks installer publication**, where previously the redeploy fired regardless. `Notify Landing` keeps its `workflow_dispatch` as the manual override.
Expand Down
2 changes: 1 addition & 1 deletion install.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ $BINARY_NAME = 'archcore'
# PostHog project key rather than comparing against the placeholder text, so the
# substitution can never accidentally rewrite its own off-switch.
$POSTHOG_KEY = '__POSTHOG_KEY__'
$POSTHOG_HOST = 'https://ph.archcore.ai'
$POSTHOG_HOST = 'https://edge.archcore.ai'

# Reported as `$lib_version` alongside `$lib`. The script is fetched fresh on
# every run and carries no other version marker, so this is the only way to tell
Expand Down
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ PINNED_VERSION="${ARCHCORE_VERSION:-}"
# rather than comparing against the placeholder text, so the substitution can
# never accidentally rewrite its own off-switch.
POSTHOG_KEY="__POSTHOG_KEY__"
POSTHOG_HOST="https://ph.archcore.ai"
POSTHOG_HOST="https://edge.archcore.ai"

# Reported as `$lib_version` alongside `$lib`. The script is fetched fresh on
# every run and carries no other version marker, so this is the only way to tell
Expand Down
8 changes: 7 additions & 1 deletion internal/telemetry/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ var apiKey string

const (
// defaultEndpoint is the PostHog capture path install.sh already posts to.
defaultEndpoint = "https://ph.archcore.ai/i/v0/e/"
//
// This is hardcoded with no environment override, so a released binary
// reports here for the rest of its life. That is why the host it names can
// only be retired once builds carrying the new one have propagated, and
// why ph.archcore.ai is kept alive alongside edge.archcore.ai — see
// analytics-host-must-reach-posthog.adr.md in archcore-ai/landing.
defaultEndpoint = "https://edge.archcore.ai/i/v0/e/"

// keyPrefix marks a real PostHog project key. The guard tests the prefix
// rather than comparing against a placeholder, so the release substitution
Expand Down
4 changes: 2 additions & 2 deletions internal/telemetry/telemetry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func TestFrozenWireIdentifiers(t *testing.T) {
got string
want string
}{
{"capture endpoint", defaultEndpoint, "https://ph.archcore.ai/i/v0/e/"},
{"capture endpoint", defaultEndpoint, "https://edge.archcore.ai/i/v0/e/"},
{"$lib", libName, "archcore-cli"},
{"source", sourceCLI, "cli"},
{"project key prefix", keyPrefix, "phc_"},
Expand Down Expand Up @@ -753,7 +753,7 @@ func TestNewClient(t *testing.T) {
if c.Version != "1.2.3" {
t.Errorf("Version = %q, want %q", c.Version, "1.2.3")
}
if want := "https://ph.archcore.ai/i/v0/e/"; c.endpoint() != want {
if want := "https://edge.archcore.ai/i/v0/e/"; c.endpoint() != want {
t.Errorf("endpoint() = %q, want %q", c.endpoint(), want)
}
if c.key() != apiKey {
Expand Down
Loading