Skip to content

Commit b9554af

Browse files
authored
fix: remove legacy licensing telemetry (#367)
1 parent cbf4b60 commit b9554af

13 files changed

Lines changed: 22 additions & 325 deletions

File tree

apps/website/content/docs/licensing/getting-started/introduction.mdx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Introduction
22

3-
`@ngaf/licensing` is the shared license-check helper used by the framework packages. It verifies compact Ed25519-signed tokens offline, evaluates the result into a small status set, emits non-blocking warnings, and sends non-blocking license telemetry when asked.
3+
`@ngaf/licensing` is the shared license-check helper used by the framework packages. It verifies compact Ed25519-signed tokens offline, evaluates the result into a small status set, and emits non-blocking warnings when appropriate.
44

55
The package itself is MIT licensed. `COMMERCIAL.md` states that the libraries in this repository are free to use, modify, and distribute in commercial and noncommercial projects. The proprietary part called out there is the internal minting service, not this package.
66

@@ -12,11 +12,10 @@ The main entry point exports:
1212
|-----|---------|
1313
| `verifyLicense()` | verifies token signature against a public key |
1414
| `evaluateLicense()` | turns a verify result and current time into a status |
15-
| `runLicenseCheck()` | verifies, evaluates, warns once, and sends non-blocking telemetry |
15+
| `runLicenseCheck()` | verifies, evaluates, and warns once |
1616
| `emitNag()` | emits the warning for non-licensed statuses |
1717
| `signLicense()` | signs claims with an Ed25519 private key |
1818
| `inferNoncommercial()` | returns a default noncommercial hint from `NODE_ENV` |
19-
| `createTelemetryClient()` | sends a license telemetry POST |
2019
| `LICENSE_PUBLIC_KEY` | bundled public key |
2120

2221
`@noble/ed25519` is the only peer dependency.
@@ -66,7 +65,6 @@ The higher-level check is designed not to block app startup:
6665

6766
- signature verification is local;
6867
- warning output goes through `console.warn` unless a custom `warn` function is supplied;
69-
- telemetry is fire-and-forget;
70-
- telemetry send failures are swallowed by the telemetry client.
68+
- no network request is made by the licensing check.
7169

7270
The code returns statuses instead of throwing for normal license states. Consumers can choose what to do with the status, but the framework packages use it as a warning and visibility mechanism, not as an app kill switch.

apps/website/content/docs/licensing/guides/ci-and-offline.mdx

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@ If you call `runLicenseCheck()` directly, inject the current time only when you
2626
```ts
2727
await runLicenseCheck({
2828
package: '@ngaf/example',
29-
version: '1.0.0',
3029
token,
3130
publicKey,
32-
telemetryEndpoint,
3331
nowSec: 1_735_689_600,
3432
});
3533
```
@@ -47,28 +45,7 @@ const result = evaluateLicense(verified, {
4745
});
4846
```
4947

50-
This does not emit warnings and does not send telemetry.
51-
52-
## Telemetry in licensing checks
53-
54-
`runLicenseCheck()` creates a telemetry client and calls `send()` without awaiting it. The telemetry body contains:
55-
56-
- package name;
57-
- package version;
58-
- license id from `claims.sub`, when present;
59-
- an anonymous instance id;
60-
- epoch-second timestamp.
61-
62-
The licensing telemetry helper opts out when either of these is set:
63-
64-
```bash
65-
CACHEPLANE_TELEMETRY=0
66-
CACHEPLANE_TELEMETRY=false
67-
```
68-
69-
It also checks `globalThis.CACHEPLANE_TELEMETRY` and treats `false`, `0`, or `"0"` as opt-out values.
70-
71-
If `fetch` is not available, the telemetry send is a no-op. If the request fails, the error is swallowed.
48+
This does not emit warnings. The higher-level `runLicenseCheck()` helper can emit warnings, but it does not make network requests.
7249

7350
## Signing tokens
7451

apps/website/content/docs/licensing/guides/setup.mdx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,8 @@ import {
2626

2727
const status = await runLicenseCheck({
2828
package: '@ngaf/example',
29-
version: '1.0.0',
3029
token: process.env.NGAF_LICENSE,
3130
publicKey: LICENSE_PUBLIC_KEY,
32-
telemetryEndpoint: 'https://telemetry.example.com/v1/ping',
3331
isNoncommercial: inferNoncommercial(),
3432
});
3533
```
@@ -57,10 +55,8 @@ You can inject a custom warning sink:
5755
```ts
5856
await runLicenseCheck({
5957
package: '@ngaf/example',
60-
version: '1.0.0',
6158
token,
6259
publicKey,
63-
telemetryEndpoint,
6460
warn: (message) => logger.warn(message),
6561
});
6662
```

apps/website/content/docs/licensing/reference/api.mdx

Lines changed: 1 addition & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,11 @@ Turns a verify result into a `LicenseStatus`.
8080
```ts
8181
interface RunLicenseCheckOptions {
8282
package: string;
83-
version: string;
8483
token?: string;
8584
publicKey: Uint8Array;
86-
telemetryEndpoint: string;
8785
nowSec?: number;
8886
isNoncommercial?: boolean;
8987
warn?: (message: string) => void;
90-
fetch?: typeof fetch;
9188
}
9289

9390
function runLicenseCheck(
@@ -99,8 +96,7 @@ Runs the full check:
9996

10097
1. verifies the token when present;
10198
2. evaluates the license status;
102-
3. emits a warning when appropriate;
103-
4. sends non-blocking telemetry.
99+
3. emits a warning when appropriate.
104100

105101
Repeated calls with the same package and token are treated as already handled and return `licensed`.
106102

@@ -138,35 +134,3 @@ function signLicense(
138134
```
139135

140136
Signs claims with an Ed25519 private key and returns the compact token consumed by `verifyLicense()`.
141-
142-
## createTelemetryClient()
143-
144-
```ts
145-
interface TelemetryEvent {
146-
package: string;
147-
version: string;
148-
licenseId?: string;
149-
}
150-
151-
interface CreateTelemetryClientOptions {
152-
endpoint: string;
153-
fetch?: typeof fetch;
154-
generateInstanceId?: () => string;
155-
}
156-
```
157-
158-
`createTelemetryClient(options).send(event)` POSTs JSON to the configured endpoint unless telemetry is opted out or `fetch` is unavailable.
159-
160-
The request body uses snake-case fields:
161-
162-
```json
163-
{
164-
"package": "@ngaf/example",
165-
"version": "1.0.0",
166-
"license_id": "cus_123",
167-
"anon_instance_id": "generated-id",
168-
"ts": 1735689600
169-
}
170-
```
171-
172-
Send failures are swallowed.

libs/chat/src/lib/provide-chat.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,6 @@ import {
88
import type { AngularRegistry } from '@ngaf/render';
99

1010
const PACKAGE_NAME = '@ngaf/chat';
11-
declare const __CACHEPLANE_CHAT_VERSION__: string | undefined;
12-
const PACKAGE_VERSION =
13-
typeof __CACHEPLANE_CHAT_VERSION__ !== 'undefined'
14-
? __CACHEPLANE_CHAT_VERSION__
15-
: '0.0.0-dev';
16-
const TELEMETRY_ENDPOINT = 'https://telemetry.cacheplane.dev/v1/ping';
1711

1812
export interface ChatConfig {
1913
/** Default render registry for generative UI components. */
@@ -42,10 +36,8 @@ export const CHAT_CONFIG = new InjectionToken<ChatConfig>('CHAT_CONFIG');
4236
export function provideChat(config: ChatConfig) {
4337
void runLicenseCheck({
4438
package: PACKAGE_NAME,
45-
version: PACKAGE_VERSION,
4639
token: config.license,
4740
publicKey: config.__licensePublicKey ?? LICENSE_PUBLIC_KEY,
48-
telemetryEndpoint: TELEMETRY_ENDPOINT,
4941
isNoncommercial:
5042
config.__licenseEnvHint?.isNoncommercial ?? inferNoncommercial(),
5143
});

libs/langgraph/src/lib/agent.provider.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,6 @@ import {
88
import { AgentTransport } from './agent.types';
99

1010
const PACKAGE_NAME = '@ngaf/langgraph';
11-
// Wired up by the release pipeline — imported lazily to avoid a hard build-time
12-
// dependency on package.json.
13-
declare const __CACHEPLANE_AGENT_VERSION__: string | undefined;
14-
const PACKAGE_VERSION =
15-
typeof __CACHEPLANE_AGENT_VERSION__ !== 'undefined'
16-
? __CACHEPLANE_AGENT_VERSION__
17-
: '0.0.0-dev';
18-
const TELEMETRY_ENDPOINT =
19-
'https://telemetry.cacheplane.dev/v1/ping';
2011

2112
/**
2213
* Global configuration for agent instances.
@@ -52,10 +43,8 @@ export function provideAgent(config: AgentConfig): Provider {
5243
// Fire-and-forget license check. Never blocks DI resolution.
5344
void runLicenseCheck({
5445
package: PACKAGE_NAME,
55-
version: PACKAGE_VERSION,
5646
token: config.license,
5747
publicKey: config.__licensePublicKey ?? LICENSE_PUBLIC_KEY,
58-
telemetryEndpoint: TELEMETRY_ENDPOINT,
5948
isNoncommercial:
6049
config.__licenseEnvHint?.isNoncommercial ?? inferNoncommercial(),
6150
});

libs/licensing/README.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# @ngaf/licensing
22

3-
Offline Ed25519 license verification + non-blocking telemetry for the Cacheplane
4-
Angular framework libraries.
3+
Offline Ed25519 license verification for the Cacheplane Angular framework
4+
libraries.
55

66
## Status
77

@@ -13,10 +13,7 @@ Private, pre-1.0. Consumed by `@ngaf/langgraph`, `@ngaf/render`, and
1313
- `verifyLicense(token, publicKey)` — pure Ed25519 verification, no I/O.
1414
- `evaluateLicense(result, { nowSec })` — returns one of
1515
`licensed | grace | expired | missing | tampered | noncommercial`.
16-
- `runLicenseCheck(options)` — runs verification, emits a single
17-
`console.warn` with the `[cacheplane]` prefix when unlicensed, and fires a
18-
non-blocking telemetry POST.
16+
- `runLicenseCheck(options)` — runs verification and emits a single
17+
`console.warn` with the `[cacheplane]` prefix when unlicensed.
1918
- **Never throws from init** — every failure mode is reported via warn, never
2019
by throwing or blocking the host application's startup.
21-
- **Opt out of telemetry** — set `CACHEPLANE_TELEMETRY=0` in the environment, or
22-
`globalThis.CACHEPLANE_TELEMETRY = false`.

libs/licensing/src/index.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,6 @@ export type { LicenseStatus, EvaluateResult, EvaluateOptions } from './lib/evalu
66
export { evaluateLicense } from './lib/evaluate-license.js';
77
export type { EmitNagOptions } from './lib/nag.js';
88
export { emitNag } from './lib/nag.js';
9-
export type {
10-
TelemetryEvent,
11-
TelemetryClient,
12-
CreateTelemetryClientOptions,
13-
} from './lib/telemetry.js';
14-
export { createTelemetryClient } from './lib/telemetry.js';
159
export type { RunLicenseCheckOptions } from './lib/run-license-check.js';
1610
export { runLicenseCheck } from './lib/run-license-check.js';
1711
export { signLicense } from './lib/sign-license.js';

libs/licensing/src/lib/run-license-check.spec.ts

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -18,48 +18,40 @@ describe('runLicenseCheck', () => {
1818
let kp: DevKeyPair;
1919
let validToken: string;
2020
let warn: ReturnType<typeof vi.fn>;
21-
let fetchMock: ReturnType<typeof vi.fn>;
2221

2322
beforeEach(async () => {
2423
kp = await generateKeyPair();
2524
validToken = await signLicense(BASE, kp.privateKey);
2625
warn = vi.fn();
27-
fetchMock = vi.fn().mockResolvedValue(new Response(null, { status: 204 }));
2826
__resetNagStateForTests();
2927
__resetRunLicenseCheckStateForTests();
3028
});
3129
afterEach(() => {
3230
__resetNagStateForTests();
3331
__resetRunLicenseCheckStateForTests();
32+
vi.restoreAllMocks();
3433
});
3534

36-
it('does not warn with a valid token and still fires telemetry', async () => {
35+
it('does not warn with a valid token and does not perform network I/O', async () => {
36+
const fetchSpy = vi.spyOn(globalThis, 'fetch');
3737
const status = await runLicenseCheck({
3838
package: '@ngaf/langgraph',
39-
version: '1.0.0',
4039
token: validToken,
4140
publicKey: kp.publicKey,
4241
nowSec: 1_900_000_000,
43-
telemetryEndpoint: 'https://t.example.com/v1',
4442
warn,
45-
fetch: fetchMock,
4643
});
4744
expect(status).toBe('licensed');
4845
expect(warn).not.toHaveBeenCalled();
49-
expect(fetchMock).toHaveBeenCalledOnce();
50-
const body = JSON.parse(fetchMock.mock.calls[0][1].body as string);
51-
expect(body.license_id).toBe('cus_abc');
46+
expect(fetchSpy).not.toHaveBeenCalled();
5247
});
5348

5449
it('warns when token is missing', async () => {
5550
const status = await runLicenseCheck({
5651
package: '@ngaf/langgraph',
57-
version: '1.0.0',
5852
publicKey: kp.publicKey,
5953
nowSec: 1_900_000_000,
60-
telemetryEndpoint: 'https://t.example.com/v1',
6154
warn,
62-
fetch: fetchMock,
6355
});
6456
expect(status).toBe('missing');
6557
expect(warn).toHaveBeenCalledOnce();
@@ -68,51 +60,40 @@ describe('runLicenseCheck', () => {
6860
it('is idempotent per (package, token) pair', async () => {
6961
await runLicenseCheck({
7062
package: '@ngaf/langgraph',
71-
version: '1.0.0',
7263
token: validToken,
7364
publicKey: kp.publicKey,
7465
nowSec: 1_900_000_000,
75-
telemetryEndpoint: 'https://t.example.com/v1',
7666
warn,
77-
fetch: fetchMock,
7867
});
7968
await runLicenseCheck({
8069
package: '@ngaf/langgraph',
81-
version: '1.0.0',
8270
token: validToken,
8371
publicKey: kp.publicKey,
8472
nowSec: 1_900_000_000,
85-
telemetryEndpoint: 'https://t.example.com/v1',
8673
warn,
87-
fetch: fetchMock,
8874
});
89-
// Second call is a no-op: no extra warn (already guarded by nag dedupe anyway),
90-
// and crucially no second telemetry POST.
91-
expect(fetchMock).toHaveBeenCalledOnce();
75+
// Second call is a no-op: no extra warn, already guarded by nag dedupe.
76+
expect(warn).not.toHaveBeenCalled();
9277
});
9378

9479
it('re-runs when token changes (e.g., after key rotation in the host)', async () => {
95-
const otherToken = await signLicense({ ...BASE, sub: 'cus_xyz' }, kp.privateKey);
96-
await runLicenseCheck({
80+
const tamperedToken = `${validToken.slice(0, -1)}x`;
81+
const first = await runLicenseCheck({
9782
package: '@ngaf/langgraph',
98-
version: '1.0.0',
9983
token: validToken,
10084
publicKey: kp.publicKey,
10185
nowSec: 1_900_000_000,
102-
telemetryEndpoint: 'https://t.example.com/v1',
10386
warn,
104-
fetch: fetchMock,
10587
});
106-
await runLicenseCheck({
88+
const second = await runLicenseCheck({
10789
package: '@ngaf/langgraph',
108-
version: '1.0.0',
109-
token: otherToken,
90+
token: tamperedToken,
11091
publicKey: kp.publicKey,
11192
nowSec: 1_900_000_000,
112-
telemetryEndpoint: 'https://t.example.com/v1',
11393
warn,
114-
fetch: fetchMock,
11594
});
116-
expect(fetchMock).toHaveBeenCalledTimes(2);
95+
expect(first).toBe('licensed');
96+
expect(second).toBe('tampered');
97+
expect(warn).toHaveBeenCalledOnce();
11798
});
11899
});

0 commit comments

Comments
 (0)