217 lines
9.7 KiB
Markdown
217 lines
9.7 KiB
Markdown
# Alpha Browser Domain Activity Implementation Plan
|
|
|
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox syntax for tracking.
|
|
|
|
**Goal:** Add explicitly consented, privacy-minimal active-tab domain timing to the browser extension and deliver immutable activity batches to Desktop.
|
|
|
|
**Architecture:** Manual captures remain unchanged. A new tracker stores mutable accumulators, immutable pending batches, and acknowledged IDs locally; Desktop accepts a separate authenticated activity endpoint and emits browser.activity.domain.
|
|
|
|
**Tech Stack:** Plain WebExtension JavaScript, Node tests, Go HTTP handler tests, shared JSON vectors.
|
|
|
|
## Global Constraints
|
|
|
|
- Passive tracking defaults to disabled and requires explicit informed consent.
|
|
- Track only normalized hostname plus duration for an active tab in a focused window.
|
|
- Never send URL, title, text, page content, keystrokes, or history.
|
|
- A pending batch is byte-for-byte immutable; acknowledge only its own ID.
|
|
- Discard ambiguous or negative clock deltas and gaps over 10 minutes.
|
|
- Use hostname-normalization-v1 for extension, Desktop receiver, exclusions, and bindings.
|
|
|
|
---
|
|
|
|
### Task 1: Define and test canonical hostname normalization
|
|
|
|
**Files:**
|
|
- Create: /home/mirivlad/git/verstak2/verstak-sdk/schemas/hostname-normalization-v1.json
|
|
- Create: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/hostname.js
|
|
- Create: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/hostname-normalization-v1.json
|
|
- Create: /home/mirivlad/git/verstak2/verstak-browser-extension/scripts/test-hostname.js
|
|
- Create: /home/mirivlad/git/verstak2/verstak-desktop/internal/core/hostname/normalize.go
|
|
- Create: /home/mirivlad/git/verstak2/verstak-desktop/internal/core/hostname/normalize_test.go
|
|
- Create: /home/mirivlad/git/verstak2/verstak-desktop/internal/core/hostname/testdata/hostname-normalization-v1.json
|
|
|
|
**Interfaces:**
|
|
- Produces JavaScript normalizeHostnameV1(value, mode).
|
|
- Produces Go hostname.NormalizeV1(value string, mode Mode) (string, error).
|
|
- Mode is URLSource for HTTP(S) URLs and BareHost for binding/exclusion input.
|
|
|
|
- [ ] **Step 1: Write the shared vector corpus and failing tests**
|
|
|
|
Include vectors for Unicode/punycode, trailing dot, port stripping/rejection,
|
|
IPv4, bracketed IPv6, localhost, one-label internal names, malformed labels,
|
|
and an overlong name:
|
|
|
|
~~~json
|
|
[
|
|
{"mode":"bare","input":"пример.рф.","want":"xn--e1afmkfd.xn--p1ai"},
|
|
{"mode":"url","input":"https://Example.COM:8443/a","want":"example.com"},
|
|
{"mode":"bare","input":"example.com:8443","error":"port"},
|
|
{"mode":"bare","input":"[::1]","want":"::1"}
|
|
]
|
|
~~~
|
|
|
|
- [ ] **Step 2: Run red**
|
|
|
|
Run:
|
|
|
|
~~~bash
|
|
cd /home/mirivlad/git/verstak2/verstak-browser-extension
|
|
node scripts/test-hostname.js
|
|
cd /home/mirivlad/git/verstak2/verstak-desktop
|
|
GOCACHE=/tmp/verstak-go-cache go test ./internal/core/hostname -count=1
|
|
~~~
|
|
|
|
Expected: both fail because the normalizers do not exist.
|
|
|
|
- [ ] **Step 3: Implement both normalizers**
|
|
|
|
Use the browser URL parser plus ASCII IDNA conversion in JavaScript. Use
|
|
net/url, net/netip, and golang.org/x/net/idna.Lookup.ToASCII in Go. Store
|
|
ASCII A-labels, lowercase names, no ports or IPv6 brackets, and reject malformed
|
|
or over-limit values.
|
|
|
|
- [ ] **Step 4: Run green and commit**
|
|
|
|
Run:
|
|
|
|
~~~bash
|
|
cd /home/mirivlad/git/verstak2/verstak-browser-extension && npm test
|
|
cd /home/mirivlad/git/verstak2/verstak-desktop && GOCACHE=/tmp/verstak-go-cache go test ./internal/core/hostname -count=1
|
|
cmp /home/mirivlad/git/verstak2/verstak-sdk/schemas/hostname-normalization-v1.json /home/mirivlad/git/verstak2/verstak-browser-extension/shared/hostname-normalization-v1.json
|
|
cmp /home/mirivlad/git/verstak2/verstak-sdk/schemas/hostname-normalization-v1.json /home/mirivlad/git/verstak2/verstak-desktop/internal/core/hostname/testdata/hostname-normalization-v1.json
|
|
git -C /home/mirivlad/git/verstak2/verstak-sdk add schemas/hostname-normalization-v1.json
|
|
git -C /home/mirivlad/git/verstak2/verstak-sdk commit -m "feat: define hostname normalization vectors"
|
|
git -C /home/mirivlad/git/verstak2/verstak-browser-extension add shared/hostname.js shared/hostname-normalization-v1.json scripts/test-hostname.js
|
|
git -C /home/mirivlad/git/verstak2/verstak-browser-extension commit -m "feat: normalize activity hostnames"
|
|
git -C /home/mirivlad/git/verstak2/verstak-desktop add internal/core/hostname
|
|
git -C /home/mirivlad/git/verstak2/verstak-desktop commit -m "feat: normalize browser hostnames"
|
|
~~~
|
|
|
|
Expected: every copied corpus passes in both implementations.
|
|
|
|
### Task 2: Add a separate authenticated Desktop activity receiver
|
|
|
|
**Files:**
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-desktop/internal/core/browserreceiver/receiver.go
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-desktop/internal/core/browserreceiver/receiver_test.go
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-desktop/internal/api/app.go
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-desktop/internal/api/app_test.go
|
|
|
|
**Interfaces:**
|
|
- Accepts POST /activities with BrowserDomainActivity.
|
|
- Emits browser.activity.domain with normalized hostname, duration, ID, and optional workspaceId.
|
|
- Returns status accepted and activityId for first delivery and duplicate delivery.
|
|
|
|
- [ ] **Step 1: Write failing handler tests**
|
|
|
|
Create a valid immutable request and then retry the same ID:
|
|
|
|
~~~go
|
|
payload := "{\"schemaVersion\":1,\"activityId\":\"a-1\",\"hostname\":\"пример.рф\",\"durationSeconds\":300,\"startedAt\":\"2026-07-12T10:00:00Z\",\"endedAt\":\"2026-07-12T10:05:00Z\"}"
|
|
req := httptest.NewRequest(http.MethodPost, "/activities", strings.NewReader(payload))
|
|
req.Header.Set("X-Verstak-Receiver-Token", "pair-token")
|
|
~~~
|
|
|
|
Assert invalid tokens, URL-like hostnames, zero or oversize duration, and a
|
|
duplicate ID do not publish a second event.
|
|
|
|
- [ ] **Step 2: Run red**
|
|
|
|
Run:
|
|
|
|
~~~bash
|
|
cd /home/mirivlad/git/verstak2/verstak-desktop
|
|
GOCACHE=/tmp/verstak-go-cache go test ./internal/core/browserreceiver -run Activity -count=1
|
|
~~~
|
|
|
|
Expected: FAIL because /activities is not routed.
|
|
|
|
- [ ] **Step 3: Implement the bounded idempotent route**
|
|
|
|
Add BrowserDomainActivity, a 30-day bounded receiver ID cache, and
|
|
handleActivity which invokes hostname.NormalizeV1. Do not reuse capture types
|
|
or persistence. In App, resolve an active exact hostname binding to workspaceId
|
|
and a path cache before Activity receives the event.
|
|
|
|
- [ ] **Step 4: Run green and commit**
|
|
|
|
Run:
|
|
|
|
~~~bash
|
|
GOCACHE=/tmp/verstak-go-cache go test ./internal/core/browserreceiver ./internal/api -count=1
|
|
git add internal/core/browserreceiver/receiver.go internal/core/browserreceiver/receiver_test.go internal/api/app.go internal/api/app_test.go
|
|
git commit -m "feat: receive browser domain activity"
|
|
~~~
|
|
|
|
Expected: authentication, normalization, idempotency, and binding tests pass.
|
|
|
|
### Task 3: Build the opt-in extension tracker and immutable delivery queue
|
|
|
|
**Files:**
|
|
- Create: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/activity-tracker.js
|
|
- Create: /home/mirivlad/git/verstak2/verstak-browser-extension/scripts/test-activity-tracker.js
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/api.js
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/background.js
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/popup/popup.html
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/popup/popup.js
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/popup/popup.css
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/locales/en.json
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-browser-extension/shared/locales/ru.json
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-browser-extension/chromium/manifest.json
|
|
- Modify: /home/mirivlad/git/verstak2/verstak-browser-extension/firefox/manifest.json
|
|
|
|
**Interfaces:**
|
|
- Persists activeAccumulator, pendingBatches, and acknowledgedIds under verstak.activityTracker.
|
|
- Produces sendActivity(receiverUrl, token, immutablePayload).
|
|
- Settings include passiveActivityEnabled false and excludedDomains empty.
|
|
|
|
- [ ] **Step 1: Write failing state-machine tests**
|
|
|
|
Cover consent, active-tab-only timing, long-gap discard, and the A/B batch case:
|
|
|
|
~~~js
|
|
tracker.addElapsed('example.com', 600, start, end);
|
|
const batchA = tracker.freeze('example.com');
|
|
tracker.addElapsed('example.com', 300, laterStart, laterEnd);
|
|
assert.equal(tracker.pendingBatches[0].payload.durationSeconds, 600);
|
|
tracker.acknowledge(batchA.activityId);
|
|
assert.equal(tracker.activeAccumulator['example.com'].durationSeconds, 300);
|
|
~~~
|
|
|
|
- [ ] **Step 2: Run red**
|
|
|
|
Run:
|
|
|
|
~~~bash
|
|
cd /home/mirivlad/git/verstak2/verstak-browser-extension
|
|
node scripts/test-activity-tracker.js
|
|
~~~
|
|
|
|
Expected: FAIL because the state machine is absent.
|
|
|
|
- [ ] **Step 3: Implement tracker, disclosure, and listeners**
|
|
|
|
Implement the tracker as pure functions. In background.js, register
|
|
tabs.onActivated, tabs.onUpdated, windows.onFocusChanged, idle.onStateChanged,
|
|
and a five-minute alarm only while opted in. Set idle detection to 600 seconds.
|
|
On a gap over 600 seconds, clock rollback, lock, or browser startup, reset the
|
|
active checkpoint without adding time. Retry frozen batches oldest-first and
|
|
remove only matching IDs.
|
|
|
|
Show an unchecked Russian/English consent explanation in settings. Add windows,
|
|
alarms, and idle permissions to both manifests. Keep manual capture queue and
|
|
popup actions unchanged.
|
|
|
|
- [ ] **Step 4: Run green and commit**
|
|
|
|
Run:
|
|
|
|
~~~bash
|
|
npm test
|
|
node scripts/test-activity-tracker.js
|
|
git add shared chromium/manifest.json firefox/manifest.json scripts/test-activity-tracker.js
|
|
git commit -m "feat: add opt-in browser domain tracker"
|
|
~~~
|
|
|
|
Expected: tracker unit tests and existing extension tests pass.
|