forbidden

@forbidden/game-telemetry (0.1.0)

Published 2026-09-03 09:21:48 +02:00 by forbidden-automation

Installation

@forbidden:registry=https://git.forbiddenprojects.dev/api/packages/forbidden/npm/
npm install @forbidden/game-telemetry@0.1.0
"@forbidden/game-telemetry": "0.1.0"

About this package

Framework-neutral, privacy-bounded client for the Forbidden Games API telemetry contract

@forbidden/game-telemetry

One framework-neutral client for the Forbidden Games API. It owns anonymous-device bootstrap, session lifecycle, page-view and mission telemetry, bounded retry, and the small browser/Godot Web adapters. Games provide only their catalog gameId, public API URL, build identifier, and gameplay signals.

The package contains no service credential. The generated anonymous device secret and backend session token are player-scoped API credentials: the identity may be stored by the caller, while the session token and retry queue stay in memory.

Browser

import { createBrowserTelemetry } from "@forbidden/game-telemetry/browser";

const telemetry = createBrowserTelemetry({
  gameId: "ashwater_parish",
  baseUrl: import.meta.env.VITE_EUGAMES_API_BASE_URL,
  build: import.meta.env.VITE_GIT_SHA,
});

await telemetry.start(); // false on failure; gameplay continues
telemetry.client.mission.start("silent_farm_01");
telemetry.client.mission.complete("silent_farm_01");

start() emits exactly one visit.start and one session.start for the backend session. An in-flight batch is immutable: its encoded body, event IDs, and request idempotency key remain stable even when a response is lost and new events arrive. A 409 is not accepted or dropped. The queue is bounded and in-memory, each encoded batch stays below 64 KiB, and an offline API never blocks the game. After bounded immediate attempts, only one delayed continuation is scheduled.

The browser adapter uses credential-capable fetch(..., { keepalive: true }) for pagehide teardown; sendBeacon cannot carry the required authorization headers. A persisted BFCache pageshow waits for teardown and starts a new resume session. Applications should use this adapter instead of adding their own unload handler.

Explicit opt-in (When the Room Blinks)

import {
  SLEEP_HORROR_EXTENSION,
  createGameTelemetryClient,
  createSleepHorrorConsent,
} from "@forbidden/game-telemetry";

const consent = createSleepHorrorConsent(localStorage); // unset means denied
const telemetry = createGameTelemetryClient({
  gameId: "sleephorror",
  baseUrl: import.meta.env.VITE_EUGAMES_API_BASE_URL,
  identityStorage: localStorage,
  consent,
  extensions: [SLEEP_HORROR_EXTENSION],
});

consent.grant();
await telemetry.start();
// consent.withdraw() aborts work, clears the queue/session and erases stored identity.

Refusing consent makes no network request. Gameplay must remain available whether consent is unset, denied, withdrawn, storage fails, or Games API is unavailable.

Godot Web

The @forbidden/game-telemetry/godot-web bridge exposes JSON-free start, resume, mission*, end, and dispose methods suitable for JavaScriptBridge. Construct the client in the web shell; do not pass a token or service credential through Godot.

Extension boundary

Core events and mission lifecycle are built in. A game-specific extension must be explicitly registered with an allowlisted name and validator. Payloads are additionally limited by byte size, depth, item count, and forbidden privacy/credential keys. The Games API contract must already accept that extension name; this is not an escape hatch for arbitrary analytics.

Publishing

The package is configured for the Forbidden organization npm registry and prepack always builds a fresh dist, including from a clean checkout. Publication belongs to the shared organization package publisher operated by forbidden-operator; repositories must not add another workflow, service user, token, or .npmrc credential. Consumers pin an immutable published version and commit the lockfile.

Version 0.1.0 is the first immutable release candidate. Its default-branch merge is the deliberate publication trigger and must happen only after the digest-pinned delivery-policy runner and the exact forbidden/eugames npm caller are live. The publisher proves that both version 0.1.0 and the full-source-SHA dist-tag are absent before mutation, then verifies registry SHA-1, SHA-512 SRI, version and source-tag readback. Any existing version, conflicting tag or ambiguous registry result fails closed instead of overwriting package state.

A default-branch publication event is single-use. If infrastructure skips the npm preparation job before it starts, do not rerun that consumed event against changed central workflow source. Land a reviewed, meaningful source-only clarification after the central fix instead; the resulting fresh default-branch SHA is then the only release candidate and provenance tag.

Release evidence distinguishes the original scoped-caller event (push) from execution inside the reusable workflow (workflow_call). Both identities are validated independently before the credential-free preparation can mark a package eligible.

Npm preparation uses the qualified delivery runner's installed delivery-contract binary directly; it does not require a host Docker socket. Before eligibility, the runner proves the exact contract API version and all packaging dependencies with a credential-free fixture.

The credential-bearing publication boundary first performs a supported, read-only package-list GET scoped exactly to owner forbidden, type npm, query @forbidden/game-telemetry, and limit 1. Only an unredirected HTTP 200 with a strict empty or exact-target package array can proceed to the immutable absence checks. This proves the package capability target without broadening the package-only token; the forbidden-operator identity remains an owner-provisioned secret invariant, not a runtime identity claim. The token crosses into the publisher only through a mode-restricted, single-link file that is removed before registry calls; it is never a command argument or part of the publication artifact.

Dependencies

Development Dependencies

ID Version
@eugames/contracts 0.0.0
typescript ^5.8.2
vitest ^3.2.4
Details
npm
2026-09-03 09:21:48 +02:00
49
UNLICENSED
sha-1ee7219c2f05ac38fabef5bfc8e43c5b8f2fe6b2
24 KiB
Assets (1)
Versions (2) View all
0.2.0 2026-09-04
0.1.0 2026-09-03