Prepare public GitHub mirror: scrub LAN IPs #10

Merged
ilia merged 2 commits from chore/github-mirror-prep into main 2026-08-05 11:35:07 -05:00
11 changed files with 18 additions and 18 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ PLAYKIT_ENV=dev
PLAYKIT_LOG_LEVEL=info
PLAYKIT_FORBID_PRIVATE_HOSTS=true
# PLAYKIT_METRICS_ENABLED=true
# PLAYKIT_PUSHGATEWAY_URL=http://10.0.10.24:9091
# PLAYKIT_PUSHGATEWAY_URL=http://10.255.255.1:9091
# Mailtrap Email Testing (Sandbox) — emails only appear if app SMTP → sandbox.smtp.mailtrap.io
# PLAYKIT_MAILTRAP_API_TOKEN=
+3 -3
View File
@@ -65,7 +65,7 @@ test('sign-out stays on public host', async ({ page, playkitConfig, timings }) =
| `PLAYKIT_PROJECT` | no | Metric / log label |
| `PLAYKIT_ENV` | no | Metric / log label (`dev`/`qa`/`prod`) |
| `PLAYKIT_METRICS_ENABLED` | no | Push timings to Pushgateway |
| `PLAYKIT_PUSHGATEWAY_URL` | if metrics | e.g. `http://10.0.10.24:9091` |
| `PLAYKIT_PUSHGATEWAY_URL` | if metrics | e.g. `http://10.255.255.1:9091` |
| `PLAYKIT_LOG_LEVEL` | no | `debug` \| `info` \| `warn` \| `error` |
**Secrets:** put test credentials and pushgateway tokens in Infisical (`LevkinOps`) and sync into Gitea Actions — see ansible `docs/hardening/SECRETS.md`. Never commit passwords.
@@ -166,7 +166,7 @@ await runPersistentSession({
`createMailInbox()` picks the provider from `PLAYKIT_MAIL_PROVIDER` (default
`mailpit`) so specs don't need to know which backend is behind it. Prefer
**Mailpit** — it's our homelab SMTP trap (`10.0.10.45`, no external
**Mailpit** — it's our homelab SMTP trap (`10.255.255.1`, no external
dependency); use Mailtrap only if you specifically want the SaaS sandbox.
```ts
@@ -184,7 +184,7 @@ assertPublicHost(link!);
```
**Important:** the mail client only sees mail if the app's SMTP actually
points at that trap (Mailpit `10.0.10.45:1025` in DEV, or Mailtrap's
points at that trap (Mailpit `10.255.255.1:1025` in DEV, or Mailtrap's
`sandbox.smtp.mailtrap.io` + inbox credentials for SaaS). Sending via Gmail to
a real address will not appear in either. See ansible `docs/hardening/SECRETS.md`
(`## Playkit / punimtag e2e secrets`).
+1 -1
View File
@@ -42,7 +42,7 @@ Store in Infisical `LevkinOps` / `Development` (path e.g. `/playkit/punimtag`):
- `PLAYKIT_BASE_URL=https://punimtagdev.levkin.ca`
- `E2E_ADMIN_EMAIL` / `E2E_ADMIN_PASSWORD` (dedicated test user — not a humans password)
- optional `PLAYKIT_PUSHGATEWAY_URL=http://10.0.10.24:9091`
- optional `PLAYKIT_PUSHGATEWAY_URL=http://10.255.255.1:9091`
- optional `PLAYKIT_RETRY_PRESET=strictCi|flakyNetwork|default`
- for mail specs: `PLAYKIT_MAIL_PROVIDER=mailpit` (default) + `MAILPIT_*`, or `MAILTRAP_*`
+1 -1
View File
@@ -6,7 +6,7 @@ Living ops/status after releases. Update when an item closes.
| Item | Status |
|------|--------|
| Pushgateway + `live-playkit` Grafana board | Applied (`10.0.10.24:9091`) |
| Pushgateway + `live-playkit` Grafana board | Applied (`10.255.255.1:9091`) |
| Tag release workflow + `RELEASE_TOKEN` | Working (`v0.3.1`, `v0.4.0` Gitea releases) |
| Selftest CI | Green (Playwright image pinned to package version) |
| Outline **QA & Dev → Playkit** @ v0.4.0 | Synced via API 2026-07-15 → https://notes.levkin.ca/doc/playkit-CrPJq5x2qQ |
+1 -1
View File
@@ -2,7 +2,7 @@
* Example UI pattern (not executed in kit CI — consumers own Playwright projects).
*
* Critical assert: after sign-out, URL host must remain the public host.
* This is the failure mode from punimtag #57 (redirect to 10.0.10.121:3001).
* This is the failure mode from punimtag #57 (redirect to 10.255.255.1:3001).
*/
import { test, expect } from '@playwright/test';
import { loadConfig, waitForUrlHost, assertPublicHost, TimingCollector } from '../../src/index.js';
+1 -1
View File
@@ -70,7 +70,7 @@ def playkit_markdown(version: str) -> str:
## Metrics
- Pushgateway: `http://10.0.10.24:9091` (LAN)
- Pushgateway: `http://10.255.255.1:9091` (LAN)
- Grafana: `dash.levkin.ca` → **Live — Playkit e2e** (`live-playkit`)
- Enable in CI with `PLAYKIT_METRICS_ENABLED=true` + `PLAYKIT_PUSHGATEWAY_URL`
+4 -4
View File
@@ -101,10 +101,10 @@ describe('waitForUrlHost', () => {
});
it('throws a descriptive error naming both hosts when the host never matches', async () => {
const page = { url: () => 'http://10.0.10.45:3000/dashboard' } as unknown as Page;
const page = { url: () => 'http://10.255.255.1:3000/dashboard' } as unknown as Page;
await expect(
waitForUrlHost(page, 'app.levkin.ca', { timeout: 0, logger: silentLogger }),
).rejects.toThrow(/Expected URL host "app\.levkin\.ca" but got "10\.0\.10\.45"/);
).rejects.toThrow(/Expected URL host "app\.levkin\.ca" but got "10\.255\.255\.1"/);
});
});
@@ -114,7 +114,7 @@ describe('assertPublicHost', () => {
expect(() => assertPublicHost('punimtagdev.levkin.ca')).not.toThrow();
});
it.each(['https://10.0.10.45:3000', 'http://localhost:3000', 'https://192.168.1.10', '127.0.0.1'])(
it.each(['https://10.255.255.1:3000', 'http://localhost:3000', 'https://192.168.1.10', '127.0.0.1'])(
'rejects private host %s',
(input) => {
expect(() => assertPublicHost(input)).toThrow(/Refusing private host/);
@@ -122,7 +122,7 @@ describe('assertPublicHost', () => {
);
it('allows private hosts when forbidPrivate is false (intentional LAN runs)', () => {
expect(() => assertPublicHost('http://10.0.10.45:3000', false)).not.toThrow();
expect(() => assertPublicHost('http://10.255.255.1:3000', false)).not.toThrow();
});
});
+2 -2
View File
@@ -5,7 +5,7 @@ import { TimingCollector } from '../metrics/index.js';
describe('isPrivateHost', () => {
it('detects LAN and localhost', () => {
expect(isPrivateHost('10.0.10.121')).toBe(true);
expect(isPrivateHost('10.255.255.1')).toBe(true);
expect(isPrivateHost('192.168.1.1')).toBe(true);
expect(isPrivateHost('172.16.0.2')).toBe(true);
expect(isPrivateHost('localhost')).toBe(true);
@@ -21,7 +21,7 @@ describe('loadConfig', () => {
it('rejects private expected host by default', () => {
expect(() =>
loadConfig({
PLAYKIT_BASE_URL: 'http://10.0.10.121:3001',
PLAYKIT_BASE_URL: 'http://10.255.255.1:3001',
}),
).toThrow(/private/);
});
+2 -2
View File
@@ -9,12 +9,12 @@ describe('loadMailpitConfig', () => {
it('loads base url and auth', () => {
expect(
loadMailpitConfig({
MAILPIT_BASE_URL: 'http://10.0.10.45:8025',
MAILPIT_BASE_URL: 'http://10.255.255.1:8025',
MAILPIT_USER: 'u',
MAILPIT_PASSWORD: 'p',
}),
).toEqual({
baseUrl: 'http://10.0.10.45:8025',
baseUrl: 'http://10.255.255.1:8025',
user: 'u',
password: 'p',
});
+1 -1
View File
@@ -49,7 +49,7 @@ function matchesSubject(subject: string, want?: string | RegExp): boolean {
* Load Mailpit config from env. Returns null if MAILPIT_BASE_URL unset.
*
* Env:
* - PLAYKIT_MAILPIT_BASE_URL / MAILPIT_BASE_URL (e.g. http://10.0.10.45:8025)
* - PLAYKIT_MAILPIT_BASE_URL / MAILPIT_BASE_URL (e.g. http://10.255.255.1:8025)
* - MAILPIT_USER / MAILPIT_PASSWORD (basic auth)
*/
export function loadMailpitConfig(env: NodeJS.ProcessEnv = process.env): MailpitConfig | null {
+1 -1
View File
@@ -78,7 +78,7 @@ export interface MetricsPushOptions {
/**
* Push metrics to a Prometheus Pushgateway.
* Example URL: http://10.0.10.24:9091
* Example URL: http://10.255.255.1:9091
*/
export async function pushPrometheusMetrics(
collector: TimingCollector,