diff --git a/.gitignore b/.gitignore index 4dc4fd14..979ea576 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ Research/ showcase/server/data/dbip-city-lite.* !showcase/server/data/dbip-city-lite.fixture.csv !showcase/server/data/dbip-city-lite.ipv6.fixture.csv +!showcase/server/data/dbip-city-lite.places.fixture.csv .claude/worktrees/ BUG-REPORT-*.md dist/skill/ diff --git a/fly.toml b/fly.toml index a22ce141..384b9df9 100644 --- a/fly.toml +++ b/fly.toml @@ -8,14 +8,16 @@ primary_region = 'sjc' PORT = '3847' DB_PATH = '/data/fsb-data.db' NODE_ENV = 'production' - # Worldwide IP->region dataset (DB-IP IP-to-City Lite, generated by - # showcase/server/scripts/refresh-dbip-dataset.mjs). Lives on the mounted - # /data volume (NOT baked into the image). Safe to set before the files exist: - # ip-geo.js degrades to 'unknown' until they are uploaded, then restart the - # machine so the lazy loader re-reads. IPv4 + sibling IPv6. See - # showcase/server/data/README.md. - DBIP_DATASET_PATH = '/data/dbip-city-lite.csv' - DBIP_IPV6_DATASET_PATH = '/data/dbip-city-lite.ipv6.csv' + # Worldwide city-level IP->place dataset (DB-IP IP-to-City Lite, generated by + # showcase/server/scripts/refresh-dbip-dataset.mjs): IPv4 + IPv6 range tables + # and the place-centroid table. They live on the mounted /data volume (NOT + # baked into the image) and are searched on disk, not loaded into memory. + # Safe to set before the files exist: ip-geo.js degrades to 'unknown' until + # they are uploaded; restart the machine after replacing a file so it is + # reopened. See showcase/server/data/README.md. + DBIP_DATASET_PATH = '/data/dbip-city/dbip-city-lite.csv' + DBIP_IPV6_DATASET_PATH = '/data/dbip-city/dbip-city-lite.ipv6.csv' + DBIP_PLACES_DATASET_PATH = '/data/dbip-city/dbip-city-lite.places.csv' [http_service] internal_port = 3847 diff --git a/showcase/angular/src/app/core/stats/fsb-telemetry.types.ts b/showcase/angular/src/app/core/stats/fsb-telemetry.types.ts index defdc73d..e2c41a65 100644 --- a/showcase/angular/src/app/core/stats/fsb-telemetry.types.ts +++ b/showcase/angular/src/app/core/stats/fsb-telemetry.types.ts @@ -11,6 +11,18 @@ export type { DatasetAvailability } from './dataset-state.types'; // has no rate limiter (it is server-cached with a 30 s memo + 60 s // Cache-Control). +/** + * One published place: 'US-CA/San Jose' (city), 'US-CA' (state), 'US' + * (country), or the 'Other' / 'unknown' buckets. `lat`/`lon` are the dataset's + * approximate centroid for the place itself, present when the server knows it. + */ +export interface FSBTelemetryRegion { + label: string; + uniq: number; + lat?: number; + lon?: number; +} + /** * Headline response shape from GET /api/public-stats/global. * @@ -62,13 +74,13 @@ export interface FSBTelemetryHeadline { /** Latest day's agent-label aggregate (currently empty until agent rollups exist). */ popular_agents: Array<{ label: string; uniq: number }>; /** Latest day's coarse region aggregate with a k>=5 floor. */ - popular_regions: Array<{ label: string; uniq: number }>; + popular_regions: FSBTelemetryRegion[]; /** * Last-known coarse region per install across the retained 365-day rollups, - * k>=5 floored. Anonymous (country / US-state labels only). Powers the globe - * so location survives the 7-day raw-event wipe. + * k>=5 floored (city, else state, else country, else 'Other'). Powers the + * globe so location survives the 7-day raw-event wipe. */ - users_by_region_365d?: Array<{ label: string; uniq: number }>; + users_by_region_365d?: FSBTelemetryRegion[]; /** Compatibility alias for avg_agents_per_reporting_user. */ avg_agents_per_user: number; /** active_agents_now / active_agents_reporting_users_now. */ diff --git a/showcase/angular/src/app/core/stats/region-geo.spec.ts b/showcase/angular/src/app/core/stats/region-geo.spec.ts new file mode 100644 index 00000000..7f517ecb --- /dev/null +++ b/showcase/angular/src/app/core/stats/region-geo.spec.ts @@ -0,0 +1,29 @@ +import { regionCentroid, regionDisplayName, regionPosition, regionSpread } from './region-geo'; + +describe('region-geo', () => { + it('falls back from a city label to its state, then its country', () => { + expect(regionCentroid('US-CA/San Jose')).toEqual(regionCentroid('US-CA')); + expect(regionCentroid('SG/Singapore')).toEqual(regionCentroid('SG')); + expect(regionCentroid('AU-Victoria/Melbourne')).toEqual(regionCentroid('AU')); + }); + + it('prefers the server centroid and never places the floor buckets', () => { + expect(regionPosition({ label: 'US-CA/San Jose', lat: 37.3, lon: -121.9 })) + .toEqual({ lon: -121.9, lat: 37.3 }); + expect(regionPosition({ label: 'US-CA' })).toEqual(regionCentroid('US-CA')); + expect(regionPosition({ label: 'Other', lat: 1, lon: 2 })).toBeNull(); + expect(regionPosition({ label: 'unknown' })).toBeNull(); + expect(regionPosition({ label: 'XX-Nowhere' })).toBeNull(); + }); + + it('spreads a city tighter than a state, and a state tighter than a country', () => { + expect(regionSpread('US-CA/San Jose')).toBeLessThan(regionSpread('US-CA')); + expect(regionSpread('US-CA')).toBeLessThan(regionSpread('US')); + }); + + it('names a city before its region', () => { + expect(regionDisplayName('US-CA/San Jose')).toBe('San Jose, US-CA'); + expect(regionDisplayName('SG/Singapore')).toBe('Singapore, SG'); + expect(regionDisplayName('US-CA')).toBe('US-CA'); + }); +}); diff --git a/showcase/angular/src/app/core/stats/region-geo.ts b/showcase/angular/src/app/core/stats/region-geo.ts index 33162094..f802cfc3 100644 --- a/showcase/angular/src/app/core/stats/region-geo.ts +++ b/showcase/angular/src/app/core/stats/region-geo.ts @@ -1,15 +1,17 @@ -// Quick task 260630-hct follow-up -- label -> approximate centroid lookup for the -// Stats page "Active now" globe. Mirrors the server's region label format from -// showcase/server/src/routes/telemetry.js (regionLabel()): +// Label -> approximate centroid lookup for the Stats page globe. Mirrors the +// server's region label format (showcase/server/src/utils/region-label.js): +// 'US-CA/San Jose' -- city, after its state (or bare country: 'SG/Singapore') // 'US-CA' -- US state, 2-letter USPS code (US_STATE_CODES there) // 'DE' -- bare ISO 3166-1 alpha-2 country code (no subdivision) // 'AU-Victoria' -- non-US country + slugged subdivision name // 'unknown'/'Other' -- never geolocatable; callers must not plot these // -// This is a coarse, best-effort lookup for visualization only (which continent / -// part of a country to glow a node in) -- NOT a precise geocoder. Countries or -// subdivisions absent from these tables simply return null so callers can skip -// them; the globe degrades gracefully rather than guessing a location. +// The server sends lat/lon for every place its dataset knows, cities included; +// these tables are the fallback when it does not. This is a coarse, best-effort +// lookup for visualization only (which continent / part of a country to glow a +// node in) -- NOT a precise geocoder. Countries or subdivisions absent from +// these tables simply return null so callers can skip them; the globe degrades +// gracefully rather than guessing a location. /** Approximate geographic centroid, in degrees. */ export interface RegionCentroid { @@ -94,6 +96,10 @@ const US_STATE_CENTROIDS: Readonly> = { export function regionCentroid(label: string): RegionCentroid | null { if (!label || label === 'unknown' || label === 'Other') return null; + // No city table here: a city label falls back to its state or country. + const slash = label.indexOf('/'); + if (slash !== -1) return regionCentroid(label.slice(0, slash)); + const dash = label.indexOf('-'); if (dash === -1) return COUNTRY_CENTROIDS[label] ?? null; @@ -104,3 +110,32 @@ export function regionCentroid(label: string): RegionCentroid | null { // country centroid so the region is still placed on the right landmass. return COUNTRY_CENTROIDS[country] ?? null; } + +/** + * Where to plot a published place: the server's centroid when it sent one, + * else the tables above. + */ +export function regionPosition(region: { + readonly label: string; + readonly lat?: number; + readonly lon?: number; +}): RegionCentroid | null { + if (region.label === 'unknown' || region.label === 'Other') return null; + const { lat, lon } = region; + if (typeof lat === 'number' && typeof lon === 'number' && Number.isFinite(lat) && Number.isFinite(lon)) { + return { lon, lat }; + } + return regionCentroid(region.label); +} + +/** Globe jitter radius in degrees: tight for a city, wide for a country. */ +export function regionSpread(label: string): number { + if (label.includes('/')) return 1.5; + return label.includes('-') ? 3 : 6; +} + +/** 'US-CA/San Jose' -> 'San Jose, US-CA'; other labels unchanged. */ +export function regionDisplayName(label: string): string { + const slash = label.indexOf('/'); + return slash === -1 ? label : `${label.slice(slash + 1)}, ${label.slice(0, slash)}`; +} diff --git a/showcase/angular/src/app/pages/privacy/privacy-history-archive.component.html b/showcase/angular/src/app/pages/privacy/privacy-history-archive.component.html index 5b2cbe5e..4cfc8bf0 100644 --- a/showcase/angular/src/app/pages/privacy/privacy-history-archive.component.html +++ b/showcase/angular/src/app/pages/privacy/privacy-history-archive.component.html @@ -1,3 +1,242 @@ +
+ + September 2026 + v0.9.91 — 365-day region retention, Region (state-level) metric (full archived text) + +
+

Archived copy of the privacy policy as it stood from September 28, 2026, before the September 29, 2026 update.

+ +

TLDR: FSB runs inside your browser, and no browsing data is collected by FSB servers. AI calls go directly from your browser to the provider you choose. API keys and credentials are encrypted locally, and memory data stays on your device. If you install the optional local agent service, FSB can also be driven by an MCP client running on your own machine over a loopback connection, and by coding-agent CLIs you have already installed. The only data that ever leaves your machine for FSB is opt-out anonymous usage telemetry that powers the public /stats page. If you pair Remote Dashboard, transient live-preview frames can pass through the relay during the session, but they are not stored. Everything is open source and auditable.

+ +

Data Collection

+

FSB operates entirely within your browser. When you initiate an automation task, the extension may inspect the active tab's DOM (Document Object Model) and, when you invoke a capability, make same-origin site API requests from your browser session.

+
    +
  • No browsing history is collected or stored beyond the current session
  • +
  • DOM data and site API results are analyzed locally and discarded after each automation step unless you explicitly save them in memory
  • +
  • No personal information is harvested from pages you visit
  • +
+ +

Site API capabilities

+

FSB's capability layer can call a site's own first-party API from the page context, using your already-authenticated browser session. These requests run locally in Chrome; first-party cookies or site auth may be attached by the browser for that target site, but FSB does not return, store, log, or send cookies, tokens, CSRF values, request bodies, or response bodies to FSB servers.

+

Capability invocations follow FSB's consent controls and are recorded only in the redacted local audit log: origin, capability slug, method, side-effect class, consent decision, and outcome. The audit log never stores invocation arguments, request bodies, response bodies, cookies, tokens, CSRF values, or site response payloads.

+ +

Chrome permissions FSB requests

+

To run web automation, FSB declares the following permissions in its Chrome manifest. Each is used only for the documented purpose; nothing is sent off-device on the strength of any of them.

+
    +
  • DOM and tabs, activeTab, scripting, tabs, windows, sidePanel, and host permission <all_urls>: read and write the active tab, inject the automation content script, list and switch tabs, and render the side panel
  • +
  • Advanced automation, debugger: attach the Chrome DevTools Protocol for coordinate-based clicks, drag, and key-hold actions that the regular DOM API cannot perform. webNavigation: observe navigation start/finish events so automation waits for the right moment
  • +
  • Local storage, storage, unlimitedStorage: store your settings, credentials, payment methods, and memory in chrome.storage.local on your device. Unlimited storage lifts the default 10 MB quota so memory and session logs can grow without hitting a wall
  • +
  • UX helpers, clipboardWrite: write copy-to-clipboard results from automation. alarms: schedule background housekeeping. offscreen: host the speech-to-text recorder in a hidden document because service workers cannot capture audio directly
  • +
  • Local service, nativeMessaging: start the optional local agent service described under Local Agent Service and Native Messaging below. Nothing about the pages you visit crosses that channel. system.memory: read the total installed RAM figure only, so FSB can suggest a sensible limit on how many agents run at once. The available-memory and per-processor breakdowns are never read, and the figure never leaves the extension
  • +
+

Microphone access for speech-to-text is not declared in the manifest. Chrome shows its own permission prompt the first time you use the mic button.

+ +

Data Storage

+

All settings and data are stored locally in Chrome's extension storage, with sensitive values encrypted at rest as described in the sections below.

+
    +
  • Configuration is stored in chrome.storage.local
  • +
  • API keys, credentials, and payment methods are encrypted before storage, each detailed in its own section below
  • +
  • Session logs are stored locally and can be cleared at any time
  • +
  • Analytics data (task counts, success rates) stays on your device
  • +
+ +

External Services

+

FSB communicates with external AI providers only when you configure and use a hosted provider. If you use LM Studio, AI requests stay on your machine through its local OpenAI-compatible server. The choice of provider and what data is sent is under your control.

+
    +
  • Hosted API calls are made only to the provider you select (xAI, OpenAI, Anthropic, Google, or OpenRouter)
  • +
  • LM Studio uses a local OpenAI-compatible server on your device and does not require an API key
  • +
  • Sent data includes: task description, DOM structure summary, and action context
  • +
  • If you pair Remote Dashboard or use legacy Background Agents sync, an optional relay server handles WebSocket messages for that session. Live-preview frames may pass through the relay transiently, but none of the following is ever persisted on an FSB server: page content, DOM data, browsing history, screenshots, AI prompts, AI responses, cookies, tokens, or site API payloads. This is opt-in only, and the rest of this policy refers back to this list rather than restating it
  • +
  • Each provider has their own privacy policy governing how they handle API requests
  • +
+ +

Local Agent Service and Native Messaging

+

FSB can be driven by an MCP client running on your own machine. That requires a small local agent service which you install yourself, and the nativeMessaging permission exists solely so the extension can start it. Chrome cannot launch a local program directly, so this is the only supported mechanism.

+
    +
  • The messaging host io.github.fullselfbrowsing.fsb_native_host is registered only by an explicit action you take, by running fsb-mcp-server install --native-host. The extension cannot install it, and its registration names one permitted extension origin
  • +
  • The exchange is a closed schema. FSB sends a version number, an action of either wake or bootstrap, and a locally generated correlation identifier; it receives that identifier back with an outcome and a reason drawn from a fixed list. Any additional field is rejected outright and messages are capped at 4 KB. No URL, tab, page content, or DOM data exists anywhere in this schema
  • +
  • A successful start returns a pairing code. This is a loopback authentication secret generated on your device and stored with owner-only permissions, rotated every time the service starts. It carries no information about you or your browsing
  • +
  • The service listens on your machine only. The extension bridge refuses to bind any address that is not loopback, and the MCP endpoint binds 127.0.0.1 by default and whenever FSB starts it
  • +
  • Under its own directory the service keeps only operational records: a diagnostics log of delegated-run outcomes restricted to a fixed field list, and a process-supervision journal. Task text, environment values, binary paths, and error text never reach either of them, and entries carrying credential-shaped values are discarded rather than written
  • +
  • If the host is not installed, the feature simply does not start. Nothing is transmitted, and the rest of the extension continues to work normally
  • +
+ +

Delegated CLI Agents

+

FSB can hand a task to a coding-agent command-line tool you have already installed, currently Claude Code or Grok Build. FSB starts that program on your own machine; your task is never proxied through FSB servers.

+
    +
  • Your task text reaches the tool over its standard input and nowhere else. FSB checks this before every run: if the task appears in the command line, the working directory, the environment, or any file path, the run is aborted rather than started. Tasks are capped at 64 KB
  • +
  • While the run is in progress the tool calls back into FSB over the loopback connection to drive your browser. Whatever it reads that way — page text, DOM snapshots, spreadsheet values, screenshots — is returned to that tool, and therefore reaches its vendor: Anthropic for Claude Code, xAI for Grok Build
  • +
  • The run uses the account already signed in to that tool. FSB does not hold, read, or transmit your Anthropic or xAI credential, and any usage or charges follow that vendor's own plan
  • +
  • Grok Build runs inside a profile directory FSB keeps separate from your own. The sign-in credential stored there is written by the Grok tool itself during its own sign-in flow
  • +
  • The agent instructions FSB supplies state that passwords, CVV values, payment-card data, and saved credentials must never enter prompts, narration, logs, or tool arguments
  • +
+ +

Reading and Filling Google Sheets

+

FSB can read and fill a range in Google Sheets by driving the spreadsheet interface in your own already-authenticated browser session.

+
    +
  • Reading a range returns those cell values to the model you selected, exactly like any other page content FSB reads on your behalf. Only the range you asked for is read; there is no whole-spreadsheet export
  • +
  • Spreadsheet content is stripped out before anything is written to FSB's local session history. Only shape counts survive — how many rows, columns, and values were involved. The spreadsheet address, sheet name, cell references, and every cell value are discarded
  • +
  • If that filtering cannot be applied to a record for any reason, the record is dropped rather than stored
  • +
+ +

Remote Dashboard and PhantomStream Live Preview

+

When you pair Remote Dashboard, FSB can show a live preview of the active browser tab using PhantomStream. The preview streams structured DOM data instead of pixels: an initial snapshot, MutationObserver diffs, scroll position, automation overlays, dialog state, media playback state, and remote-control messages over WebSocket.

+
    +
  • The relay forwards those live-preview frames only to the paired dashboard session, and nothing from the preview stream is persisted on FSB servers — see the list under External Services above
  • +
  • FSB enables PhantomStream input masking (maskInputs: true), so passwords and form-control values are masked before they leave the captured page
  • +
  • The dashboard viewer renders mirrored content in a scriptless sandbox and sanitizes mirrored DOM and CSS before display
  • +
  • Images, video, and audio are mirrored by reference in current dashboard mode. Media bytes do not cross the relay; the viewer may fetch allowed public HTTPS asset or media URLs directly, while private, internal, non-HTTPS, or otherwise blocked origins are replaced with placeholders by PhantomStream's fail-closed fetch policy
  • +
+ +

No Third-Party Tracking

+

FSB does not include any third-party analytics, ad trackers, or cross-site fingerprinting. There are no cookies and no third-party scripts beyond the AI provider APIs you explicitly configure. The one piece of first-party data FSB sends home is the opt-out Anonymous Usage Telemetry described below, used solely to power the public /stats dashboard.

+ +

API Keys

+

Your API keys are encrypted locally using AES-GCM before being stored. They are never transmitted anywhere except to the AI provider you configured, and only as authentication headers in API requests.

+
    +
  • Keys are encrypted at rest in Chrome storage
  • +
  • Decryption only happens in-memory when making API calls
  • +
  • Keys are never logged, exported, or shared
  • +
+ +

Auto-Passwords

+

FSB includes an optional credential manager that stores login credentials encrypted on your device. Passwords are never exposed to AI models. They are filled directly into pages by the content script, bypassing the AI entirely.

+
    +
  • Credentials are encrypted at rest using AES-GCM with 256-bit keys and PBKDF2 key derivation
  • +
  • When the AI analyzes a page, password field values are replaced with [hidden]. The actual password is never included in any AI prompt
  • +
  • Auto-fill is performed by the content script injecting values directly into the DOM, with no AI involvement in the credential flow
  • +
  • The credential list view only shows usernames and domains. Passwords are decrypted individually and only when needed for auto-fill
  • +
  • Credentials are stored per-domain with parent domain fallback (e.g., accounts.google.com inherits from google.com)
  • +
+ +

Payment Methods

+

FSB includes an optional payment-method vault that stores card details on your device for checkout auto-fill. Cards are treated with the same encryption and AI isolation as login credentials, and the full card number is never sent to any AI model.

+
    +
  • Card details (number, expiry, cardholder, and zip) are encrypted at rest using AES-GCM with the same vault-derived key used for credentials
  • +
  • When the AI analyzes a checkout page, any detected card-number field values are replaced with [hidden] before the prompt is built. Card numbers, CVV, and expiry are never included in any AI prompt
  • +
  • Auto-fill happens via the content script writing directly into the page's DOM fields, bypassing the AI entirely
  • +
  • The list view shows only a card nickname and last-4 digits. Full numbers are decrypted in memory only at the moment of fill
  • +
  • An MCP client can request a payment fill via use_payment_method, but the user is shown an in-extension confirmation prompt before any card data is written into the page
  • +
  • CVV is never persisted unless you opt in per-card, and even then it is encrypted alongside the rest of the record
  • +
+ +

Speech-to-Text

+

FSB includes an optional microphone input for the prompt box. The default provider runs entirely in your browser; an optional OpenAI Whisper fallback can be enabled in settings if you want higher accuracy.

+
    +
  • Default provider: the browser's native SpeechRecognition API. Audio is processed by Chrome and never leaves your device through FSB
  • +
  • Optional Whisper provider: when sttProvider is set to whisper and an OpenAI key is configured, recorded audio chunks are uploaded directly from your browser to OpenAI's transcription endpoint. FSB never sees or stores the audio
  • +
  • The microphone is only active while you are holding or have toggled the mic button. Chrome prompts for permission the first time you use it; FSB does not request microphone access in the extension manifest
  • +
  • Transcripts are inserted into the prompt textarea only and are never logged, persisted, or transmitted outside the active AI request you choose to send
  • +
  • Disable speech entirely by leaving the mic button untouched, or by clearing the optional Whisper provider in Chrome extension storage
  • +
+ +

Prompt Injection Prevention

+

Web pages can contain hidden text designed to hijack AI agents. FSB implements multi-layered defenses to ensure the AI only follows your instructions, never instructions embedded in page content.

+
    +
  • All page content is wrapped in [PAGE_CONTENT] boundary markers, and the AI is instructed to never follow instructions found within these markers
  • +
  • A sanitization engine strips known injection patterns (e.g., "ignore previous instructions", fake system prompts, override attempts) from all page content before it reaches the AI
  • +
  • AI-generated actions are validated before execution. Dangerous URLs (javascript:, data:) and script injection attempts are blocked
  • +
  • Only a strict, fixed allowlist of known tools can be executed. The AI cannot invent or call arbitrary actions
  • +
  • Content size is capped (500 chars per value, 15K total prompt cap) to limit payload delivery
  • +
  • Invisible Unicode control characters that websites embed are stripped before processing
  • +
+ +

Background Agents and Server Sync

+

Deprecated in v0.9.45rc1. FSB's built-in Background Agents have been superseded by OpenClaw and Claude Routines, with remote control now handled by the Sync tab. The disclosures below are retained for users still running v0.9.44 or earlier; on current builds the relay server is only contacted when you pair a Sync session.

+

If you opt into legacy Background Agents server sync or pair Remote Dashboard, a relay server facilitates communication between your extension and the dashboard.

+
    +
  • The server stores: agent definitions (name, schedule, target URL), run metrics (token count, cost, duration, success/fail status), and session pairing tokens
  • +
  • Beyond that run metadata the server persists nothing derived from the pages you visit — see the list under External Services above
  • +
  • Authentication uses hash keys (generated locally) and session tokens that expire after 24 hours
  • +
  • One-time pairing tokens expire after 60 seconds and cannot be reused
  • +
  • Server sync is disabled by default. You must explicitly enable it in Options
  • +
+ +

Memory System

+

FSB's memory system stores navigation patterns and site intelligence to improve automation over time.

+
    +
  • All memory data (semantic, episodic, procedural) is stored locally in chrome.storage.local
  • +
  • No memory data is sent to any external server
  • +
  • Memory can be viewed and cleared at any time from the Options dashboard
  • +
  • Site maps and navigation patterns are domain-specific and isolated from each other
  • +
+ +

Session Replay and Screenshots

+

When an MCP client drives FSB, the extension records what the agent did so you can review or replay the run afterwards. These records stay on your device.

+
    +
  • Runs still in progress are held in Chrome's session storage so they survive the service worker being evicted; finished runs are written to chrome.storage.local through the same history store your own automation runs use
  • +
  • Records are redacted before they are stored. Values under credential-shaped keys, text typed into fields that look like password, one-time-code, or card inputs, and signed or token-bearing URL parameters are all replaced. Ordinary addresses and selectors are preserved, because replay does not work without them
  • +
  • Recorded runs are kept for 30 days by default, adjustable between 1 and 365 days, and older ones are pruned by a daily job. Recording can be switched off entirely in Advanced Settings
  • +
  • Screenshot images are never written into that history. Only capture metadata such as dimensions and byte size is retained
  • +
  • Screenshots an MCP client requests are saved by the local service as files on your own disk with owner-only permissions and deleted automatically after seven days. The image is also handed back to the requesting client, and therefore to that client's model
  • +
+ +

Anonymous Usage Telemetry

+

FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.

+ +

What we collect

+
    +
  • A random per-install UUID stored in chrome.storage.local under the key fsbInstallUuid. The UUID is generated locally and never tied to your identity.
  • +
  • The name of the MCP client used (e.g. Claude Code, Cursor, Codex), drawn from a fixed allowlist.
  • +
  • The model name used for a session (e.g. grok-4-fast, claude-opus-4), drawn from a fixed allowlist.
  • +
  • Aggregate input/output token counts per session.
  • +
  • The number of active FSB agents on your install (an integer count).
  • +
+ +

What we do NOT collect

+
    +
  • Page URLs, hostnames, or browsing history.
  • +
  • Prompts, instructions, task descriptions, or any natural-language text you send to your model provider.
  • +
  • Page DOM, screenshots, page content, site API payloads, or AI responses.
  • +
  • Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.
  • +
  • Names, usernames, account handles, or any free-form identity fields.
  • +
  • Email addresses, phone numbers, or contact information.
  • +
+ +

Region (state-level) metric

+
    +
  • The server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IP IP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.
  • +
  • Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs.
  • +
  • The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).
  • +
+ +

Retention

+

Raw events are retained for 7 days. Daily rollups (one row per install per day, including the coarse region label) are retained for 365 days. Global aggregates (one row per day, no per-install dimension) are retained indefinitely so historical charts on /stats remain stable.

+ +

How to opt out

+

Open the FSB Control Panel, scroll to Advanced Settings, and toggle Send anonymous usage data off. The change takes effect immediately; no further events will be sent from your install.

+ +

How to erase your data

+

To request erasure of all telemetry rows associated with your install (GDPR Article 17), look up your fsbInstallUuid in Chrome DevTools → Application → Storage → Extension storage, then send a single HTTP request:

+
curl -X POST -H "Content-Type: application/json" \
+-d '{{ '{' }}"install_uuid":"<your-uuid>"{{ '}' }}' \
+https://full-selfbrowsing.com/api/telemetry/forget
+ +

Limited Use affirmation

+

FSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.

+ +

Aggregated public metrics

+

We publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.

+ +

Consent and Audit Controls

+

FSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.

+
    +
  • The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.
  • +
  • Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.
  • +
  • Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.
  • +
+ +

Open Source

+

FSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.

+ +

Changes to This Policy

+

If this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.

+ +

Contact

+

If you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues.

+
+
+
August 2026 diff --git a/showcase/angular/src/app/pages/privacy/privacy-page.component.html b/showcase/angular/src/app/pages/privacy/privacy-page.component.html index 1532561e..93cbf023 100644 --- a/showcase/angular/src/app/pages/privacy/privacy-page.component.html +++ b/showcase/angular/src/app/pages/privacy/privacy-page.component.html @@ -3,7 +3,7 @@

Privacy Policy

-

Last updated: September 2026

+

Last updated: September 29, 2026

@@ -194,7 +194,7 @@

Session Replay and Screenshots<

Anonymous Usage Telemetry

-

FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.

+

FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse place label — city, state or province, and country — that the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (city-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.

What we collect

    @@ -210,16 +210,17 @@

    What we do NOT collect

  • Page URLs, hostnames, or browsing history.
  • Prompts, instructions, task descriptions, or any natural-language text you send to your model provider.
  • Page DOM, screenshots, page content, site API payloads, or AI responses.
  • -
  • Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.
  • +
  • Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse city, state, and country label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.
  • Names, usernames, account handles, or any free-form identity fields.
  • Email addresses, phone numbers, or contact information.
-

Region (state-level) metric

+

Region (city-level) metric

    -
  • The server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IP IP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.
  • -
  • Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs.
  • -
  • The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).
  • +
  • The server derives a coarse place label — country, state or province, and city — from your request IP at ingest, using a self-hosted DB-IP IP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.
  • +
  • Region is published only in aggregate, behind a k≥5 anonymity floor applied level by level: an install is counted under its city when at least 5 distinct installs share that city, otherwise under its state or province, then its country, by the same rule. Anything still below 5 collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). Each install is counted in exactly one bucket, and no published city, state, or country label ever represents fewer than 5 installs.
  • +
  • The /stats globe places each published label at the approximate center of that city, state, or country, taken from the same dataset — never at the location of any install.
  • +
  • The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, street address, or coordinates — a city name is the finest detail kept; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).

Retention

diff --git a/showcase/angular/src/app/pages/stats/stats-page.component.html b/showcase/angular/src/app/pages/stats/stats-page.component.html index 8a2d8297..d2e421d9 100644 --- a/showcase/angular/src/app/pages/stats/stats-page.component.html +++ b/showcase/angular/src/app/pages/stats/stats-page.component.html @@ -101,7 +101,7 @@

Stats @if (hasPlottableRegions) { - Where installs were last seen, by coarse region, over the past 365 days. + Where installs were last seen over the past 365 days, by city, state, or country. Place data by DB-IP. } @else { Not enough anonymous activity yet to break down by region — check back soon. } diff --git a/showcase/angular/src/app/pages/stats/stats-page.component.scss b/showcase/angular/src/app/pages/stats/stats-page.component.scss index 6d7e252f..fd7a604b 100644 --- a/showcase/angular/src/app/pages/stats/stats-page.component.scss +++ b/showcase/angular/src/app/pages/stats/stats-page.component.scss @@ -331,6 +331,12 @@ margin-top: 0.2rem; } +.globe-annotation a { + color: inherit; + text-decoration: underline; + text-underline-offset: 2px; +} + @media (max-width: 900px) { .globe-mount { height: 352px; diff --git a/showcase/angular/src/app/pages/stats/stats-page.component.spec.ts b/showcase/angular/src/app/pages/stats/stats-page.component.spec.ts index 9b3f88b3..be98d778 100644 --- a/showcase/angular/src/app/pages/stats/stats-page.component.spec.ts +++ b/showcase/angular/src/app/pages/stats/stats-page.component.spec.ts @@ -284,6 +284,39 @@ describe('StatsPageComponent visualization lifecycle', () => { ); }); + it('plots published places at the server centroid, tight for a city', async () => { + const fixture = await createFixture('fsb-active-now'); + fsb.headline$.next(readyState(fsbHeadline({ + users_by_region_365d: [ + { label: 'US-CA/San Jose', uniq: 5, lat: 37.3, lon: -121.9 }, + { label: 'JP', uniq: 6, lat: 36.2, lon: 138.3 }, + { label: 'US-TX', uniq: 5 }, + { label: 'Other', uniq: 25 }, + ], + }))); + await settle(fixture); + + expect(globe.setupGlobe.calls.mostRecent().args[1]).toEqual([ + { lon: -121.9, lat: 37.3, spread: 1.5, count: 2 }, + { lon: 138.3, lat: 36.2, spread: 6, count: 2 }, + { lon: -99.3, lat: 31.5, spread: 3, count: 2 }, + ]); + expect(fixture.componentInstance.hasPlottableRegions).toBeTrue(); + expect(fixture.componentInstance.accessibleGlobeData.map((d) => d.label)) + .toEqual(['San Jose, US-CA', 'JP', 'US-TX', 'Other']); + }); + + it('shows the empty-globe caption when only Other is published', async () => { + const fixture = await createFixture('fsb-active-now'); + fsb.headline$.next(readyState(fsbHeadline({ + users_by_region_365d: [{ label: 'Other', uniq: 55 }], + }))); + await settle(fixture); + + expect(globe.setupGlobe.calls.mostRecent().args[1]).toEqual([]); + expect(fixture.componentInstance.hasPlottableRegions).toBeFalse(); + }); + it('does not mount a chart when the loader resolves after destruction', async () => { const fixture = await createFixture(); github.stars$.next(starState()); diff --git a/showcase/angular/src/app/pages/stats/stats-page.component.ts b/showcase/angular/src/app/pages/stats/stats-page.component.ts index 7288f002..88fe0481 100644 --- a/showcase/angular/src/app/pages/stats/stats-page.component.ts +++ b/showcase/angular/src/app/pages/stats/stats-page.component.ts @@ -46,6 +46,7 @@ import { FSBTelemetryService } from '../../core/stats/fsb-telemetry.service'; import { DatasetState as FSBDatasetState, FSBTelemetryHeadline, + FSBTelemetryRegion, FSBTelemetrySeries, } from '../../core/stats/fsb-telemetry.types'; import { @@ -60,7 +61,7 @@ import { StatsViewDataState, updateStatsSourceState, } from '../../core/stats/stats-view.model'; -import { regionCentroid } from '../../core/stats/region-geo'; +import { regionDisplayName, regionPosition, regionSpread } from '../../core/stats/region-geo'; import { GlobeVisualizationService } from '../../core/globe/globe-visualization.service'; import { GlobeRegion } from '../../core/globe/globe-visualization.types'; import { LanguagePickerComponent } from '../../layout/language-picker/language-picker.component'; @@ -288,13 +289,13 @@ export class StatsPageComponent implements OnInit, OnDestroy { } } - // The server emits geographic identifiers ('DE', 'US-CA', 'AU-Victoria') that - // stay as-is, plus two English sentinels that are prose and must not. - // See region-geo.ts for the full label contract. + // The server emits geographic identifiers ('DE', 'US-CA', 'US-CA/San Jose') + // that stay untranslated, plus two English sentinels that are prose and must + // not. See region-geo.ts for the full label contract. displayLabel(label: string): string { if (label === 'unknown') return $localize`:@@stats.label.unknown:Unknown`; if (label === 'Other') return $localize`:@@stats.label.other:Other`; - return label; + return regionDisplayName(label); } get accessibleGlobeData(): readonly AccessibleDatum[] { @@ -305,7 +306,7 @@ export class StatsPageComponent implements OnInit, OnDestroy { } get globeAriaLabel(): string { - return $localize`:@@stats.globe.aria:Globe showing where FSB installs were last seen over the past 365 days, by coarse region`; + return $localize`:@@stats.globe.aria:Globe showing where FSB installs were last seen over the past 365 days, by city, state, or country`; } get tabMetrics(): readonly TabMetric[] { @@ -415,10 +416,10 @@ export class StatsPageComponent implements OnInit, OnDestroy { // an explicit "still gathering data" message when this is false rather // than silently showing a globe with no nodes. get hasPlottableRegions(): boolean { - return this.globeRegionList.some((r) => regionCentroid(r.label) !== null); + return this.globeRegionList.some((r) => regionPosition(r) !== null); } - private get globeRegionList(): readonly { label: string; uniq: number }[] { + private get globeRegionList(): readonly FSBTelemetryRegion[] { const persistent = this.latestFsbHeadline?.users_by_region_365d; if (persistent && persistent.length > 0) return persistent; return this.latestFsbHeadline?.popular_regions ?? []; @@ -884,25 +885,25 @@ export class StatsPageComponent implements OnInit, OnDestroy { } } - // Redesign -- maps the k>=5-anonymity-floored popular_regions breakdown - // (see fsb-telemetry.types.ts) to globe node clusters. Labels that can't be + // Redesign -- maps the k>=5-anonymity-floored region breakdown (see + // fsb-telemetry.types.ts) to globe node clusters. Labels that can't be // geolocated (unmapped, or the literal 'unknown'/'Other' k-floor buckets) // are skipped rather than guessed. `count` is a coarse, capped scale of // `uniq` (the k-floor already guarantees uniq >= 5 for any real entry) so - // one dominant region can't visually swamp the globe; `spread` is a fixed - // moderate jitter radius since we only have a single centroid per label, - // not a real distribution. + // one dominant region can't visually swamp the globe; `spread` jitters nodes + // around the single centroid we have per label -- tight for a city, wider + // for a state or country. private buildGlobeRegions(): GlobeRegion[] { const list = this.globeRegionList; const regions: GlobeRegion[] = []; - for (const { label, uniq } of list) { - const centroid = regionCentroid(label); - if (!centroid) continue; + for (const region of list) { + const position = regionPosition(region); + if (!position) continue; regions.push({ - lon: centroid.lon, - lat: centroid.lat, - spread: 6, - count: Math.min(16, Math.max(2, Math.round(uniq / 5))), + lon: position.lon, + lat: position.lat, + spread: regionSpread(region.label), + count: Math.min(16, Math.max(2, Math.round(region.uniq / 5))), }); } return regions; diff --git a/showcase/angular/src/locale/messages.de.xlf b/showcase/angular/src/locale/messages.de.xlf index 871f6109..5ce138f9 100644 --- a/showcase/angular/src/locale/messages.de.xlf +++ b/showcase/angular/src/locale/messages.de.xlf @@ -5515,7 +5515,7 @@ Letzter Snapshot: src/app/pages/dashboard/dashboard-page.component.ts - 3994 + 4014 @@ -5523,7 +5523,7 @@ Status: src/app/pages/dashboard/dashboard-page.component.ts - 3998 + 4018 @@ -5531,7 +5531,7 @@ Grund: src/app/pages/dashboard/dashboard-page.component.ts - 4002 + 4022 @@ -5539,7 +5539,7 @@ Wiederherstellung seit s src/app/pages/dashboard/dashboard-page.component.ts - 4006 + 4026 @@ -5547,7 +5547,7 @@ letztes Bild: vor s src/app/pages/dashboard/dashboard-page.component.ts - 4012 + 4032 @@ -5555,7 +5555,7 @@ Änderungen: src/app/pages/dashboard/dashboard-page.component.ts - 4013 + 4033 @@ -5563,7 +5563,7 @@ Fehler beim Anwenden: src/app/pages/dashboard/dashboard-page.component.ts - 4014 + 4034 @@ -5571,7 +5571,7 @@ veraltet: src/app/pages/dashboard/dashboard-page.component.ts - 4015 + 4035 @@ -5579,7 +5579,7 @@ Keine Übertragungsdaten src/app/pages/dashboard/dashboard-page.component.ts - 4016 + 4036 @@ -5587,7 +5587,7 @@ Anfrage mit Status fehlgeschlagen src/app/pages/dashboard/dashboard-page.component.ts - 4319 + 4339 @@ -5595,7 +5595,7 @@ ~ Min. verbleibend src/app/pages/dashboard/dashboard-page.component.ts - 4474 + 4494 @@ -5603,7 +5603,7 @@ ~ Sek. verbleibend src/app/pages/dashboard/dashboard-page.component.ts - 4475 + 4495 @@ -5611,7 +5611,7 @@ Läuft seit src/app/pages/dashboard/dashboard-page.component.ts - 4549 + 4569 @@ -5619,7 +5619,7 @@ Vom Benutzer angehalten – zuvor: src/app/pages/dashboard/dashboard-page.component.ts - 4554 + 4574 @@ -9542,12 +9542,32 @@ 307 + + September 2026v0.9.91 — 365-day region retention, Region (state-level) metric (full archived text) + September 2026v0.9.91 — 365-Tage-Aufbewahrung der Region, Region (auf Bundeslandebene) (vollständiger archivierter Text) + + src/app/pages/privacy/privacy-history-archive.component.html + 3,6 + + + + Archived copy of the privacy policy as it stood from September 28, 2026, before the September 29, 2026 update.TLDR: FSB runs inside your browser, and no browsing data is collected by FSB servers. AI calls go directly from your browser to the provider you choose. API keys and credentials are encrypted locally, and memory data stays on your device. If you install the optional local agent service, FSB can also be driven by an MCP client running on your own machine over a loopback connection, and by coding-agent CLIs you have already installed. The only data that ever leaves your machine for FSB is opt-out anonymous usage telemetry that powers the public /stats page. If you pair Remote Dashboard, transient live-preview frames can pass through the relay during the session, but they are not stored. Everything is open source and auditable.Data CollectionFSB operates entirely within your browser. When you initiate an automation task, the extension may inspect the active tab's DOM (Document Object Model) and, when you invoke a capability, make same-origin site API requests from your browser session.No browsing history is collected or stored beyond the current sessionDOM data and site API results are analyzed locally and discarded after each automation step unless you explicitly save them in memoryNo personal information is harvested from pages you visitSite API capabilitiesFSB's capability layer can call a site's own first-party API from the page context, using your already-authenticated browser session. These requests run locally in Chrome; first-party cookies or site auth may be attached by the browser for that target site, but FSB does not return, store, log, or send cookies, tokens, CSRF values, request bodies, or response bodies to FSB servers.Capability invocations follow FSB's consent controls and are recorded only in the redacted local audit log: origin, capability slug, method, side-effect class, consent decision, and outcome. The audit log never stores invocation arguments, request bodies, response bodies, cookies, tokens, CSRF values, or site response payloads.Chrome permissions FSB requestsTo run web automation, FSB declares the following permissions in its Chrome manifest. Each is used only for the documented purpose; nothing is sent off-device on the strength of any of them.DOM and tabs, activeTab, scripting, tabs, windows, sidePanel, and host permission <all_urls>: read and write the active tab, inject the automation content script, list and switch tabs, and render the side panelAdvanced automation, debugger: attach the Chrome DevTools Protocol for coordinate-based clicks, drag, and key-hold actions that the regular DOM API cannot perform. webNavigation: observe navigation start/finish events so automation waits for the right momentLocal storage, storage, unlimitedStorage: store your settings, credentials, payment methods, and memory in chrome.storage.local on your device. Unlimited storage lifts the default 10 MB quota so memory and session logs can grow without hitting a wallUX helpers, clipboardWrite: write copy-to-clipboard results from automation. alarms: schedule background housekeeping. offscreen: host the speech-to-text recorder in a hidden document because service workers cannot capture audio directlyLocal service, nativeMessaging: start the optional local agent service described under Local Agent Service and Native Messaging below. Nothing about the pages you visit crosses that channel. system.memory: read the total installed RAM figure only, so FSB can suggest a sensible limit on how many agents run at once. The available-memory and per-processor breakdowns are never read, and the figure never leaves the extensionMicrophone access for speech-to-text is not declared in the manifest. Chrome shows its own permission prompt the first time you use the mic button.Data StorageAll settings and data are stored locally in Chrome's extension storage, with sensitive values encrypted at rest as described in the sections below.Configuration is stored in chrome.storage.localAPI keys, credentials, and payment methods are encrypted before storage, each detailed in its own section belowSession logs are stored locally and can be cleared at any timeAnalytics data (task counts, success rates) stays on your deviceExternal ServicesFSB communicates with external AI providers only when you configure and use a hosted provider. If you use LM Studio, AI requests stay on your machine through its local OpenAI-compatible server. The choice of provider and what data is sent is under your control.Hosted API calls are made only to the provider you select (xAI, OpenAI, Anthropic, Google, or OpenRouter)LM Studio uses a local OpenAI-compatible server on your device and does not require an API keySent data includes: task description, DOM structure summary, and action contextIf you pair Remote Dashboard or use legacy Background Agents sync, an optional relay server handles WebSocket messages for that session. Live-preview frames may pass through the relay transiently, but none of the following is ever persisted on an FSB server: page content, DOM data, browsing history, screenshots, AI prompts, AI responses, cookies, tokens, or site API payloads. This is opt-in only, and the rest of this policy refers back to this list rather than restating itEach provider has their own privacy policy governing how they handle API requestsLocal Agent Service and Native MessagingFSB can be driven by an MCP client running on your own machine. That requires a small local agent service which you install yourself, and the nativeMessaging permission exists solely so the extension can start it. Chrome cannot launch a local program directly, so this is the only supported mechanism.The messaging host io.github.fullselfbrowsing.fsb_native_host is registered only by an explicit action you take, by running fsb-mcp-server install --native-host. The extension cannot install it, and its registration names one permitted extension originThe exchange is a closed schema. FSB sends a version number, an action of either wake or bootstrap, and a locally generated correlation identifier; it receives that identifier back with an outcome and a reason drawn from a fixed list. Any additional field is rejected outright and messages are capped at 4 KB. No URL, tab, page content, or DOM data exists anywhere in this schemaA successful start returns a pairing code. This is a loopback authentication secret generated on your device and stored with owner-only permissions, rotated every time the service starts. It carries no information about you or your browsingThe service listens on your machine only. The extension bridge refuses to bind any address that is not loopback, and the MCP endpoint binds 127.0.0.1 by default and whenever FSB starts itUnder its own directory the service keeps only operational records: a diagnostics log of delegated-run outcomes restricted to a fixed field list, and a process-supervision journal. Task text, environment values, binary paths, and error text never reach either of them, and entries carrying credential-shaped values are discarded rather than writtenIf the host is not installed, the feature simply does not start. Nothing is transmitted, and the rest of the extension continues to work normallyDelegated CLI AgentsFSB can hand a task to a coding-agent command-line tool you have already installed, currently Claude Code or Grok Build. FSB starts that program on your own machine; your task is never proxied through FSB servers.Your task text reaches the tool over its standard input and nowhere else. FSB checks this before every run: if the task appears in the command line, the working directory, the environment, or any file path, the run is aborted rather than started. Tasks are capped at 64 KBWhile the run is in progress the tool calls back into FSB over the loopback connection to drive your browser. Whatever it reads that way — page text, DOM snapshots, spreadsheet values, screenshots — is returned to that tool, and therefore reaches its vendor: Anthropic for Claude Code, xAI for Grok BuildThe run uses the account already signed in to that tool. FSB does not hold, read, or transmit your Anthropic or xAI credential, and any usage or charges follow that vendor's own planGrok Build runs inside a profile directory FSB keeps separate from your own. The sign-in credential stored there is written by the Grok tool itself during its own sign-in flowThe agent instructions FSB supplies state that passwords, CVV values, payment-card data, and saved credentials must never enter prompts, narration, logs, or tool argumentsReading and Filling Google SheetsFSB can read and fill a range in Google Sheets by driving the spreadsheet interface in your own already-authenticated browser session.Reading a range returns those cell values to the model you selected, exactly like any other page content FSB reads on your behalf. Only the range you asked for is read; there is no whole-spreadsheet exportSpreadsheet content is stripped out before anything is written to FSB's local session history. Only shape counts survive — how many rows, columns, and values were involved. The spreadsheet address, sheet name, cell references, and every cell value are discardedIf that filtering cannot be applied to a record for any reason, the record is dropped rather than storedRemote Dashboard and PhantomStream Live PreviewWhen you pair Remote Dashboard, FSB can show a live preview of the active browser tab using PhantomStream. The preview streams structured DOM data instead of pixels: an initial snapshot, MutationObserver diffs, scroll position, automation overlays, dialog state, media playback state, and remote-control messages over WebSocket.The relay forwards those live-preview frames only to the paired dashboard session, and nothing from the preview stream is persisted on FSB servers — see the list under External Services aboveFSB enables PhantomStream input masking (maskInputs: true), so passwords and form-control values are masked before they leave the captured pageThe dashboard viewer renders mirrored content in a scriptless sandbox and sanitizes mirrored DOM and CSS before displayImages, video, and audio are mirrored by reference in current dashboard mode. Media bytes do not cross the relay; the viewer may fetch allowed public HTTPS asset or media URLs directly, while private, internal, non-HTTPS, or otherwise blocked origins are replaced with placeholders by PhantomStream's fail-closed fetch policyNo Third-Party TrackingFSB does not include any third-party analytics, ad trackers, or cross-site fingerprinting. There are no cookies and no third-party scripts beyond the AI provider APIs you explicitly configure. The one piece of first-party data FSB sends home is the opt-out Anonymous Usage Telemetry described below, used solely to power the public /stats dashboard.API KeysYour API keys are encrypted locally using AES-GCM before being stored. They are never transmitted anywhere except to the AI provider you configured, and only as authentication headers in API requests.Keys are encrypted at rest in Chrome storageDecryption only happens in-memory when making API callsKeys are never logged, exported, or sharedAuto-PasswordsFSB includes an optional credential manager that stores login credentials encrypted on your device. Passwords are never exposed to AI models. They are filled directly into pages by the content script, bypassing the AI entirely.Credentials are encrypted at rest using AES-GCM with 256-bit keys and PBKDF2 key derivationWhen the AI analyzes a page, password field values are replaced with [hidden]. The actual password is never included in any AI promptAuto-fill is performed by the content script injecting values directly into the DOM, with no AI involvement in the credential flowThe credential list view only shows usernames and domains. Passwords are decrypted individually and only when needed for auto-fillCredentials are stored per-domain with parent domain fallback (e.g., accounts.google.com inherits from google.com)Payment MethodsFSB includes an optional payment-method vault that stores card details on your device for checkout auto-fill. Cards are treated with the same encryption and AI isolation as login credentials, and the full card number is never sent to any AI model.Card details (number, expiry, cardholder, and zip) are encrypted at rest using AES-GCM with the same vault-derived key used for credentialsWhen the AI analyzes a checkout page, any detected card-number field values are replaced with [hidden] before the prompt is built. Card numbers, CVV, and expiry are never included in any AI promptAuto-fill happens via the content script writing directly into the page's DOM fields, bypassing the AI entirelyThe list view shows only a card nickname and last-4 digits. Full numbers are decrypted in memory only at the moment of fillAn MCP client can request a payment fill via use_payment_method, but the user is shown an in-extension confirmation prompt before any card data is written into the pageCVV is never persisted unless you opt in per-card, and even then it is encrypted alongside the rest of the recordSpeech-to-TextFSB includes an optional microphone input for the prompt box. The default provider runs entirely in your browser; an optional OpenAI Whisper fallback can be enabled in settings if you want higher accuracy.Default provider: the browser's native SpeechRecognition API. Audio is processed by Chrome and never leaves your device through FSBOptional Whisper provider: when sttProvider is set to whisper and an OpenAI key is configured, recorded audio chunks are uploaded directly from your browser to OpenAI's transcription endpoint. FSB never sees or stores the audioThe microphone is only active while you are holding or have toggled the mic button. Chrome prompts for permission the first time you use it; FSB does not request microphone access in the extension manifestTranscripts are inserted into the prompt textarea only and are never logged, persisted, or transmitted outside the active AI request you choose to sendDisable speech entirely by leaving the mic button untouched, or by clearing the optional Whisper provider in Chrome extension storagePrompt Injection PreventionWeb pages can contain hidden text designed to hijack AI agents. FSB implements multi-layered defenses to ensure the AI only follows your instructions, never instructions embedded in page content.All page content is wrapped in [PAGE_CONTENT] boundary markers, and the AI is instructed to never follow instructions found within these markersA sanitization engine strips known injection patterns (e.g., "ignore previous instructions", fake system prompts, override attempts) from all page content before it reaches the AIAI-generated actions are validated before execution. Dangerous URLs (javascript:, data:) and script injection attempts are blockedOnly a strict, fixed allowlist of known tools can be executed. The AI cannot invent or call arbitrary actionsContent size is capped (500 chars per value, 15K total prompt cap) to limit payload deliveryInvisible Unicode control characters that websites embed are stripped before processingBackground Agents and Server SyncDeprecated in v0.9.45rc1. FSB's built-in Background Agents have been superseded by OpenClaw and Claude Routines, with remote control now handled by the Sync tab. The disclosures below are retained for users still running v0.9.44 or earlier; on current builds the relay server is only contacted when you pair a Sync session.If you opt into legacy Background Agents server sync or pair Remote Dashboard, a relay server facilitates communication between your extension and the dashboard.The server stores: agent definitions (name, schedule, target URL), run metrics (token count, cost, duration, success/fail status), and session pairing tokensBeyond that run metadata the server persists nothing derived from the pages you visit — see the list under External Services aboveAuthentication uses hash keys (generated locally) and session tokens that expire after 24 hoursOne-time pairing tokens expire after 60 seconds and cannot be reusedServer sync is disabled by default. You must explicitly enable it in OptionsMemory SystemFSB's memory system stores navigation patterns and site intelligence to improve automation over time.All memory data (semantic, episodic, procedural) is stored locally in chrome.storage.localNo memory data is sent to any external serverMemory can be viewed and cleared at any time from the Options dashboardSite maps and navigation patterns are domain-specific and isolated from each otherSession Replay and ScreenshotsWhen an MCP client drives FSB, the extension records what the agent did so you can review or replay the run afterwards. These records stay on your device.Runs still in progress are held in Chrome's session storage so they survive the service worker being evicted; finished runs are written to chrome.storage.local through the same history store your own automation runs useRecords are redacted before they are stored. Values under credential-shaped keys, text typed into fields that look like password, one-time-code, or card inputs, and signed or token-bearing URL parameters are all replaced. Ordinary addresses and selectors are preserved, because replay does not work without themRecorded runs are kept for 30 days by default, adjustable between 1 and 365 days, and older ones are pruned by a daily job. Recording can be switched off entirely in Advanced SettingsScreenshot images are never written into that history. Only capture metadata such as dimensions and byte size is retainedScreenshots an MCP client requests are saved by the local service as files on your own disk with owner-only permissions and deleted automatically after seven days. The image is also handed back to the requesting client, and therefore to that client's modelAnonymous Usage TelemetryFSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.What we collectA random per-install UUID stored in chrome.storage.local under the key fsbInstallUuid. The UUID is generated locally and never tied to your identity.The name of the MCP client used (e.g. Claude Code, Cursor, Codex), drawn from a fixed allowlist.The model name used for a session (e.g. grok-4-fast, claude-opus-4), drawn from a fixed allowlist.Aggregate input/output token counts per session.The number of active FSB agents on your install (an integer count).What we do NOT collectPage URLs, hostnames, or browsing history.Prompts, instructions, task descriptions, or any natural-language text you send to your model provider.Page DOM, screenshots, page content, site API payloads, or AI responses.Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.Names, usernames, account handles, or any free-form identity fields.Email addresses, phone numbers, or contact information.Region (state-level) metricThe server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs.The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).RetentionRaw events are retained for 7 days. Daily rollups (one row per install per day, including the coarse region label) are retained for 365 days. Global aggregates (one row per day, no per-install dimension) are retained indefinitely so historical charts on /stats remain stable.How to opt outOpen the FSB Control Panel, scroll to Advanced Settings, and toggle Send anonymous usage data off. The change takes effect immediately; no further events will be sent from your install.How to erase your dataTo request erasure of all telemetry rows associated with your install (GDPR Article 17), look up your fsbInstallUuid in Chrome DevTools → Application → Storage → Extension storage, then send a single HTTP request:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Consent and Audit ControlsFSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. + Archivierte Fassung der Datenschutzrichtlinie in der ab dem 28. September 2026 geltenden Form, vor der Aktualisierung vom 29. September 2026.Kurzfassung: FSB läuft in deinem Browser, und es werden keine Browserdaten von FSB-Servern erfasst. KI-Aufrufe gehen direkt von deinem Browser an den von dir gewählten Anbieter. API-Schlüssel und Zugangsdaten werden lokal verschlüsselt, und Speicherdaten bleiben auf deinem Gerät. Wenn du den optionalen lokalen Agent-Dienst installierst, kann FSB auch von einem MCP-Client gesteuert werden, der auf deinem eigenen Rechner über eine Loopback-Verbindung läuft, sowie von bereits installierten Coding-Agent-CLIs. Die einzigen Daten, die deinen Rechner jemals für FSB verlassen, sind die abwählbare anonyme Nutzungstelemetrie, die die öffentliche Seite /stats speist. Wenn du Remote Dashboard koppelst, können vorübergehende Live-Vorschaubilder während der Sitzung über das Relay laufen, werden aber nicht gespeichert. Alles ist quelloffen und überprüfbar.DatenerfassungFSB wird vollständig in Ihrem Browser ausgeführt. Wenn Sie eine Automatisierungsaufgabe starten, kann die Erweiterung das DOM (Document Object Model) des aktiven Tabs untersuchen und beim Aufruf einer Fähigkeit gleichursprüngliche Website-API-Anfragen aus Ihrer Browsersitzung stellen.Es wird kein Browser-Verlauf über die aktuelle Sitzung hinaus erfasst oder gespeichertDOM-Daten und Ergebnisse von Website-API werden lokal analysiert und nach jedem Automatisierungsschritt verworfen, sofern Sie sie nicht ausdrücklich im Gedächtnis speichernEs werden keine persönlichen Informationen aus den von Ihnen besuchten Seiten gesammeltFähigkeiten für Website-APIFSB-Fähigkeitsschicht kann aus dem Seitenkontext die eigene First-Party-API einer Website über Ihre bereits authentifizierte Browsersitzung aufrufen. Diese Anfragen werden lokal in Chrome ausgeführt. Der Browser kann für die Zielwebsite First-Party-Cookies oder eine Website-Authentifizierung anhängen; FSB gibt jedoch weder Cookies, Token, CSRF-Werte noch Anfrage- oder Antworttexte zurück und speichert, protokolliert oder sendet sie auch nicht an FSB-Server.Fähigkeitsaufrufe unterliegen den Einwilligungssteuerungen von FSB und werden ausschließlich im geschwärzten lokalen Prüfprotokoll aufgezeichnet: Ursprung, Fähigkeitskennung, Methode, Nebenwirkungsklasse, Einwilligungsentscheidung und Ergebnis. Das Prüfprotokoll speichert niemals Aufrufargumente, Anfrage- oder Antworttexte, Cookies, Token, CSRF-Werte oder Nutzdaten von Website-Antworten.Chrome-Berechtigungen, die FSB anfordertUm Web-Automatisierung auszuführen, deklariert FSB die folgenden Berechtigungen in seinem Chrome-Manifest. Jede wird nur für den dokumentierten Zweck verwendet; auf ihrer Grundlage wird nichts vom Gerät übertragen.DOM und Tabs, activeTab, scripting, tabs, windows, sidePanel, sowie die Hostberechtigung <all_urls>: den aktiven Tab lesen und beschreiben, das Automatisierungs-Inhaltsskript einfügen, Tabs auflisten und wechseln sowie die Seitenleiste darstellenErweiterte Automatisierung, debugger: das Chrome DevTools Protocol für koordinatenbasierte Klicks, Zieh- und Gedrückthalten-Aktionen anbinden, die die reguläre DOM API nicht ausführen kann. webNavigation: Start- und Endereignisse der Navigation beobachten, damit die Automatisierung auf den richtigen Moment wartetLokaler Speicher, storage, unlimitedStorage: Ihre Einstellungen, Anmeldedaten, Zahlungsmethoden und das Gedächtnis in chrome.storage.local auf Ihrem Gerät speichern. Unbegrenzter Speicher hebt das standardmäßige Kontingent von 10 MB auf, damit Gedächtnis und Sitzungsprotokolle wachsen können, ohne an eine Grenze zu stoßenUX-Hilfen, clipboardWrite: Ergebnisse der Automatisierung in die Zwischenablage schreiben. alarms: Hintergrundwartung planen. offscreen: den Spracherkennungsrekorder in einem ausgeblendeten Dokument ausführen, da Service Worker Audio nicht direkt erfassen könnenLokaler Dienst, nativeMessaging: startet den optionalen lokalen Agent-Dienst, der unter Lokaler Agent-Dienst und Native Messaging weiter unten beschrieben wird. Nichts über die von dir besuchten Seiten läuft über diesen Kanal. system.memory: liest ausschließlich den Wert des insgesamt installierten RAM, damit FSB eine sinnvolle Obergrenze für gleichzeitig laufende Agenten vorschlagen kann. Der verfügbare Speicher und die Aufschlüsselung pro Prozessor werden nie gelesen, und der Wert verlässt die Erweiterung nieDer Mikrofonzugriff für Speech-to-Text ist nicht im Manifest deklariert. Chrome zeigt beim ersten Klick auf die Mikrofon-Schaltfläche seine eigene Berechtigungsanfrage an.DatenspeicherungAlle Einstellungen und Daten werden lokal im Erweiterungsspeicher von Chrome abgelegt, wobei sensible Werte im Ruhezustand verschlüsselt sind, wie in den folgenden Abschnitten beschrieben.Die Konfiguration wird in chrome.storage.local gespeichertAPI-Schlüssel, Zugangsdaten und Zahlungsmethoden werden vor der Speicherung verschlüsselt; Einzelheiten stehen jeweils im eigenen Abschnitt weiter untenSitzungsprotokolle werden lokal gespeichert und können jederzeit gelöscht werdenAnalytikdaten (Aufgabenanzahl, Erfolgsraten) bleiben auf Ihrem GerätExterne DiensteFSB kommuniziert nur dann mit externen KI-Anbietern, wenn Sie einen gehosteten Anbieter konfigurieren und nutzen. Wenn Sie LM Studio verwenden, bleiben KI-Anfragen über dessen lokalen OpenAI-kompatiblen Server auf Ihrem Rechner. Die Wahl des Anbieters und die gesendeten Daten liegen in Ihrer Hand.Gehostete API-Aufrufe gehen ausschließlich an den von Ihnen gewählten Anbieter (xAI, OpenAI, Anthropic, Google oder OpenRouter)LM Studio nutzt einen lokalen OpenAI-kompatiblen Server auf Ihrem Gerät und benötigt keinen API-KeyGesendete Daten umfassen: Aufgabenbeschreibung, DOM-Strukturzusammenfassung und AktionskontextWenn du Remote Dashboard koppelst oder die alte Background-Agents-Synchronisierung nutzt, verarbeitet ein optionaler Relay-Server die WebSocket-Nachrichten dieser Sitzung. Live-Vorschaubilder können vorübergehend über das Relay laufen, aber nichts davon wird jemals auf einem FSB-Server gespeichert: Seiteninhalte, DOM-Daten, Browserverlauf, Screenshots, KI-Prompts, KI-Antworten, Cookies, Tokens oder Site-API-Nutzdaten. Dies ist ausschließlich Opt-in, und der Rest dieser Richtlinie verweist auf diese Liste, statt sie zu wiederholenJeder Anbieter hat eigene Datenschutzbestimmungen, die regeln, wie er API-Anfragen behandeltLokaler Agent-Dienst und Native MessagingFSB kann von einem MCP-Client gesteuert werden, der auf deinem eigenen Rechner läuft. Dafür ist ein kleiner lokaler Agent-Dienst nötig, den du selbst installierst, und die Berechtigung nativeMessaging besteht ausschließlich dazu, dass die Erweiterung ihn starten kann. Chrome kann kein lokales Programm direkt starten, daher ist dies der einzige unterstützte Mechanismus.Der Messaging-Host io.github.fullselfbrowsing.fsb_native_host wird nur durch eine ausdrückliche Aktion von dir registriert, indem du fsb-mcp-server install --native-host ausführst. Die Erweiterung kann ihn nicht installieren, und seine Registrierung nennt genau einen zulässigen ErweiterungsursprungDer Austausch folgt einem geschlossenen Schema. FSB sendet eine Versionsnummer, eine Aktion entweder wake oder bootstrap sowie eine lokal erzeugte Korrelationskennung; zurück kommt dieselbe Kennung mit einem Ergebnis und einem Grund aus einer festen Liste. Jedes zusätzliche Feld wird rundweg abgelehnt, und Nachrichten sind auf 4 KB begrenzt. Weder URL noch Tab, Seiteninhalt oder DOM-Daten kommen in diesem Schema überhaupt vorEin erfolgreicher Start liefert einen Kopplungscode zurück. Dabei handelt es sich um ein Loopback-Authentifizierungsgeheimnis, das auf deinem Gerät erzeugt, mit Zugriff nur für den Eigentümer gespeichert und bei jedem Start des Dienstes neu erzeugt wird. Es enthält keinerlei Informationen über dich oder dein SurfverhaltenDer Dienst lauscht ausschließlich auf deinem Rechner. Die Erweiterungsbrücke weigert sich, an eine Adresse zu binden, die nicht Loopback ist, und der MCP-Endpunkt bindet standardmäßig und immer dann, wenn 127.0.0.1 ihn startet, an FSBIn seinem eigenen Verzeichnis führt der Dienst ausschließlich Betriebsaufzeichnungen: ein Diagnoseprotokoll der Ergebnisse delegierter Läufe, beschränkt auf eine feste Feldliste, sowie ein Prozessüberwachungsjournal. Aufgabentexte, Umgebungswerte, Binärpfade und Fehlertexte gelangen in keines von beiden, und Einträge mit zugangsdatenartigen Werten werden verworfen statt geschriebenIst der Host nicht installiert, startet die Funktion schlicht nicht. Es wird nichts übertragen, und der Rest der Erweiterung funktioniert normal weiterDelegierte CLI-AgentenFSB kann eine Aufgabe an ein Coding-Agent-Kommandozeilenwerkzeug übergeben, das du bereits installiert hast, derzeit Claude Code oder Grok Build. FSB startet dieses Programm auf deinem eigenen Rechner; deine Aufgabe wird niemals über FSB-Server geleitet.Dein Aufgabentext erreicht das Werkzeug ausschließlich über dessen Standardeingabe. FSB prüft das vor jedem Lauf: Taucht die Aufgabe in der Kommandozeile, im Arbeitsverzeichnis, in der Umgebung oder in irgendeinem Dateipfad auf, wird der Lauf abgebrochen statt gestartet. Aufgaben sind auf 64 KB begrenztWährend der Lauf aktiv ist, ruft das Werkzeug über die Loopback-Verbindung zurück in FSB, um deinen Browser zu steuern. Alles, was es auf diesem Weg liest — Seitentext, DOM-Snapshots, Tabellenwerte, Screenshots —, wird an dieses Werkzeug zurückgegeben und gelangt damit zu dessen Anbieter: Anthropic bei Claude Code, xAI bei Grok BuildDer Lauf nutzt das Konto, das in diesem Werkzeug bereits angemeldet ist. FSB hält, liest oder überträgt deine Zugangsdaten für Anthropic oder xAI nicht, und jegliche Nutzung oder Kosten richten sich nach dem Tarif des jeweiligen AnbietersGrok Build läuft in einem Profilverzeichnis, das FSB von deinem eigenen getrennt hält. Die dort abgelegten Anmeldedaten schreibt das Grok-Werkzeug während seines eigenen Anmeldevorgangs selbstDie von FSB mitgelieferten Agent-Anweisungen legen fest, dass Passwörter, CVV-Werte, Zahlungskartendaten und gespeicherte Zugangsdaten niemals in Prompts, Erzählungen, Protokolle oder Werkzeugargumente gelangen dürfenLesen und Befüllen von Google SheetsFSB kann einen Bereich in Google Sheets lesen und befüllen, indem es die Tabellenoberfläche in deiner bereits authentifizierten Browsersitzung bedient.Beim Lesen eines Bereichs werden diese Zellwerte an das von dir gewählte Modell gesendet, genau wie jeder andere Seiteninhalt, den FSB in deinem Auftrag liest. Es wird nur der von dir angeforderte Bereich gelesen; einen Export der gesamten Tabelle gibt es nichtTabelleninhalte werden entfernt, bevor irgendetwas in den lokalen Sitzungsverlauf von FSB geschrieben wird. Erhalten bleiben nur Umfangszahlen — wie viele Zeilen, Spalten und Werte beteiligt waren. Die Tabellenadresse, der Blattname, Zellbezüge und jeder einzelne Zellwert werden verworfenLässt sich diese Filterung aus irgendeinem Grund nicht auf einen Datensatz anwenden, wird der Datensatz verworfen statt gespeichertRemote Dashboard und PhantomStream-Live-VorschauWenn Sie Remote Dashboard koppeln, kann FSB mithilfe von PhantomStream eine Live-Vorschau des aktiven Browser-Tabs anzeigen. Die Vorschau streamt strukturierte DOM-Daten statt Pixeln: einen anfänglichen Snapshot, MutationObserver-Diffs, Scrollposition, Automatisierungsüberlagerungen, Dialogstatus, Medienwiedergabestatus und Fernsteuerungsnachrichten über WebSocket.Das Relay leitet diese Live-Vorschaubilder nur an die gekoppelte Dashboard-Sitzung weiter, und nichts aus dem Vorschaustream wird auf FSB-Servern gespeichert — siehe die Liste unter Externe Dienste weiter obenFSB aktiviert die Eingabemaskierung von PhantomStream (maskInputs: true); dadurch werden Passwörter und Werte von Formularelementen maskiert, bevor sie die erfasste Seite verlassenDie Ansicht der Übersicht rendert gespiegelte Inhalte in einer skriptlosen Sandbox und bereinigt gespiegeltes DOM sowie CSS vor der AnzeigeBilder, Video und Audio werden im aktuellen Übersichtsmodus per Verweis gespiegelt. Mediendaten durchlaufen das Relay nicht; die Ansicht kann erlaubte öffentliche HTTPS-URLs für Assets oder Medien direkt abrufen. Private, interne, nicht HTTPS-basierte oder anderweitig gesperrte Ursprünge werden dagegen durch Platzhalter ersetzt; dafür sorgt die im Fehlerfall sperrende Abrufrichtlinie von PhantomStreamKein Tracking durch DritteFSB enthält keinerlei Drittanbieter-Analytik, Anzeigen-Tracker oder seitenübergreifendes Fingerprinting. Es gibt keine Cookies und keine Drittanbieter-Skripte über die von Ihnen ausdrücklich konfigurierten AI-Anbieter-APIs hinaus. Die einzige First-Party-Datenmenge, die FSB übermittelt, ist die unten beschriebene Opt-out-Anonymous-Usage-Telemetry, die ausschließlich zur Bereitstellung des öffentlichen /stats-Dashboards dient.API-SchlüsselIhre API-Keys werden vor der Speicherung lokal mit AES-GCM verschlüsselt. Sie werden nirgendwohin übertragen außer an den von Ihnen konfigurierten KI-Anbieter, und nur als Authentifizierungs-Header in API-Anfragen.Keys sind im Chrome-Speicher im Ruhezustand verschlüsseltDie Entschlüsselung erfolgt nur im Arbeitsspeicher beim API-AufrufKeys werden niemals protokolliert, exportiert oder geteiltAuto-PasswörterFSB enthält einen optionalen Zugangsdaten-Manager, der Anmeldedaten verschlüsselt auf Ihrem Gerät speichert. Passwörter werden niemals an KI-Modelle weitergegeben. Sie werden direkt vom Content Script in die Seiten eingefügt und umgehen die KI vollständig.Zugangsdaten werden mit AES-GCM mit 256-Bit-Schlüsseln und PBKDF2-Schlüsselableitung im Ruhezustand verschlüsseltWenn die KI eine Seite analysiert, werden Werte von Passwortfeldern durch [hidden] ersetzt. Das echte Passwort taucht in keinem KI-Prompt aufDas Inhaltsskript füllt Werte automatisch aus, indem es sie direkt in das DOM einfügt; die KI ist nicht am Anmeldedatenfluss beteiligtDie Listenansicht der Zugangsdaten zeigt nur Benutzernamen und Domains. Passwörter werden nur einzeln und bei Bedarf für das Auto-Ausfüllen entschlüsseltZugangsdaten werden pro Domain mit Eltern-Domain-Fallback gespeichert (z. B. accounts.google.com erbt von google.com)ZahlungsmethodenFSB enthält einen optionalen Zahlungsmethoden-Tresor, der Kartendaten auf Ihrem Gerät für das Auto-Ausfüllen beim Checkout speichert. Karten werden mit derselben Verschlüsselung und KI-Isolierung wie Zugangsdaten behandelt, und die vollständige Kartennummer wird niemals an ein KI-Modell gesendet.Kartendaten (Nummer, Gültigkeit, Karteninhaber und PLZ) werden mit demselben aus dem Tresor abgeleiteten Schlüssel, der auch für Zugangsdaten verwendet wird, mit AES-GCM ruhend verschlüsseltWenn die KI eine Checkout-Seite analysiert, werden alle erkannten Kartennummern-Feldwerte vor dem Bau des Prompts durch [hidden] ersetzt. Kartennummern, CVV und Gültigkeit werden niemals in einen KI-Prompt aufgenommenDas Auto-Ausfüllen erfolgt, indem das Content-Script direkt in die DOM-Felder der Seite schreibt und die KI dabei vollständig umgangen wirdDie Listenansicht zeigt nur einen Kartenspitznamen und die letzten 4 Ziffern. Vollständige Nummern werden ausschließlich im Speicher und nur zum Zeitpunkt des Ausfüllens entschlüsseltEin MCP-Client kann ein Zahlungs-Auto-Ausfüllen über use_payment_method anfordern, dem Benutzer wird jedoch eine Bestätigungsaufforderung in der Erweiterung angezeigt, bevor Kartendaten in die Seite geschrieben werdenCVV wird niemals dauerhaft gespeichert, es sei denn, Sie aktivieren dies pro Karte; selbst dann wird er zusammen mit dem restlichen Datensatz verschlüsseltSpracherkennungFSB enthält eine optionale Mikrofoneingabe für das Prompt-Feld. Der Standardanbieter läuft vollständig in Ihrem Browser; in den Einstellungen können Sie optional einen OpenAI Whisper-Fallback aktivieren, wenn Sie höhere Genauigkeit wünschen.Standardanbieter: die native SpeechRecognition API. Audio wird von Chrome verarbeitet und verlässt Ihr Gerät niemals über FSBOptionaler Whisper-Anbieter: Wenn sttProvider auf whisper gesetzt ist und ein OpenAI-Key konfiguriert ist, werden aufgezeichnete Audioabschnitte direkt von Ihrem Browser an den Transkriptions-Endpunkt von OpenAI hochgeladen. FSB sieht oder speichert das Audio niemalsDas Mikrofon ist nur aktiv, während Sie die Mikrofon-Schaltfläche gedrückt halten oder umgeschaltet haben. Chrome fragt beim ersten Mal nach Berechtigung; FSB fordert im Erweiterungsmanifest keinen Mikrofonzugriff anTranskripte werden ausschließlich in das Prompt-Textfeld eingefügt und niemals außerhalb der aktiven KI-Anfrage, die Sie selbst abschicken, protokolliert, gespeichert oder übertragenSie können die Sprachfunktion vollständig deaktivieren, indem Sie die Mikrofon-Schaltfläche unberührt lassen oder den optionalen Whisper-Anbieter im Chrome-Erweiterungsspeicher löschenSchutz vor Prompt InjectionWebseiten können versteckten Text enthalten, der KI-Agenten kapern soll. FSB setzt mehrschichtige Schutzmaßnahmen ein, damit die KI ausschließlich Ihren Anweisungen folgt und keine in Seiteninhalten eingebetteten Anweisungen befolgt.Alle Seiteninhalte werden in [PAGE_CONTENT]-Begrenzungsmarker eingebettet, und die KI wird angewiesen, niemals Anweisungen innerhalb dieser Marker zu befolgenEine Sanitisierungs-Engine entfernt bekannte Injection-Muster (z. B. "Ignoriere vorherige Anweisungen", gefälschte Systemprompts, Override-Versuche) aus allen Seiteninhalten, bevor sie die KI erreichenVon der KI generierte Aktionen werden vor der Ausführung validiert. Gefährliche URLs (javascript:, data:) und Skript-Injection-Versuche werden blockiertNur eine strenge, feste Positivliste bekannter Werkzeuge kann ausgeführt werden. Die KI kann keine beliebigen Aktionen erfinden oder aufrufen.Die Inhaltsgröße ist begrenzt (500 Zeichen pro Wert, 15 K Gesamtprompt-Limit), um Payload-Übertragungen zu erschwerenUnsichtbare Unicode-Steuerzeichen, die Webseiten einbetten, werden vor der Verarbeitung entferntHintergrund-Agenten und Server-SynchronisationVeraltet seit v0.9.45rc1. FSB integrierte Background Agents wurden durch OpenClaw und Claude Routines ersetzt; die Fernsteuerung erfolgt jetzt über den Tab Sync. Die folgenden Angaben bleiben für Benutzer erhalten, die noch v0.9.44 oder älter verwenden. In aktuellen Builds wird der Relay-Server nur kontaktiert, wenn Sie eine Sync-Sitzung koppeln.Wenn Sie die veraltete Serversynchronisierung für Background Agents aktivieren oder Remote Dashboard koppeln, vermittelt ein Relay-Server die Kommunikation zwischen Ihrer Erweiterung und der Übersicht.Der Server speichert: Agentendefinitionen (Name, Zeitplan, Ziel-URL), Laufmetriken (Token-Anzahl, Kosten, Dauer, Erfolg/Fehler-Status) und Sitzungs-Pairing-TokensÜber diese Lauf-Metadaten hinaus speichert der Server nichts, was aus den von dir besuchten Seiten stammt — siehe die Liste unter Externe Dienste weiter obenDie Authentifizierung verwendet (lokal erzeugte) Hash-Keys und Sitzungs-Tokens, die nach 24 Stunden ablaufenEinmal-Pairing-Tokens laufen nach 60 Sekunden ab und können nicht wiederverwendet werdenDie Server-Synchronisation ist standardmäßig deaktiviert. Sie müssen sie in den Optionen ausdrücklich aktivierenSpeichersystemDas Speichersystem von FSB sichert Navigationsmuster und Site-Intelligenz, um die Automatisierung mit der Zeit zu verbessern.Alle Speicherdaten (semantisch, episodisch, prozedural) werden lokal in chrome.storage.local abgelegtKeine Speicherdaten werden an einen externen Server übertragenDer Speicher kann jederzeit im Optionen-Dashboard angesehen und gelöscht werdenSite-Maps und Navigationsmuster sind domainspezifisch und voneinander isoliertSitzungswiedergabe und ScreenshotsWenn ein MCP-Client FSB steuert, zeichnet die Erweiterung auf, was der Agent getan hat, damit du den Lauf später ansehen oder wiedergeben kannst. Diese Aufzeichnungen bleiben auf deinem Gerät.Noch laufende Durchläufe liegen im Sitzungsspeicher von Chrome, damit sie das Verdrängen des Service Workers überstehen; abgeschlossene Durchläufe werden über chrome.storage.local in denselben Verlaufsspeicher geschrieben, den auch deine eigenen Automatisierungsläufe nutzenAufzeichnungen werden vor dem Speichern bereinigt. Werte unter zugangsdatenartigen Schlüsseln, Text, der in Felder eingegeben wurde, die nach Passwort, Einmalcode oder Karte aussehen, sowie signierte oder tokenhaltige URL-Parameter werden allesamt ersetzt. Gewöhnliche Adressen und Selektoren bleiben erhalten, weil die Wiedergabe ohne sie nicht funktioniertAufgezeichnete Durchläufe werden standardmäßig 30 Tage aufbewahrt, einstellbar zwischen 1 und 365 Tagen, und ältere werden von einem täglichen Job entfernt. Die Aufzeichnung lässt sich in den erweiterten Einstellungen vollständig abschaltenScreenshot-Bilder werden nie in diesen Verlauf geschrieben. Erhalten bleiben nur Aufnahme-Metadaten wie Abmessungen und BytegrößeVon einem MCP-Client angeforderte Screenshots speichert der lokale Dienst als Dateien auf deiner eigenen Festplatte mit Zugriff nur für den Eigentümer und löscht sie automatisch nach sieben Tagen. Das Bild wird außerdem an den anfragenden Client zurückgegeben und gelangt damit zu dessen ModellAnonyme NutzungstelemetrieFSB v0.9.69 hat eine opt-out-fähige Pipeline für anonyme Nutzungstelemetrie eingeführt, damit das Projekt aggregierte Adoptionszahlen veröffentlichen kann (siehe /stats), ohne jemals auf die von dir besuchten Seiten zuzugreifen. Das einzige Standortsignal ist eine grobe Land-/Bundesland-Kennung, die der Server beim Eingang aus deiner Anfrage-IP ableitet (niemals aus Seiteninhalten) und nur aggregiert veröffentlicht – siehe Region (auf Bundeslandebene) weiter unten. Telemetrie ist standardmäßig aktiv, kann aber mit einem einzigen Schalter deaktiviert werden, und die Daten pro Installation können auf Anfrage gelöscht werden.Was wir erfassenEine zufällige UUID pro Installation, gespeichert in chrome.storage.local unter dem Schlüssel fsbInstallUuid. Die UUID wird lokal generiert und niemals mit deiner Identität verknüpft.Der Name des verwendeten MCP-Clients (z. B. Claude Code, Cursor, Codex), aus einer festen Allowlist gezogen.Der Modellname, der in einer Sitzung verwendet wird (z. B. grok-4-fast, claude-opus-4), aus einer festen Allowlist gezogen.Aggregierte Eingabe-/Ausgabe-Token-Zählungen pro Sitzung.Die Anzahl aktiver FSB-Agenten auf deiner Installation (ein ganzzahliger Wert).Was wir NICHT erfassenSeiten-URLs, Hostnamen oder Browserverlauf.Prompts, Anweisungen, Aufgabenbeschreibungen oder beliebigen natürlichsprachlichen Text, den du an deinen Modellanbieter sendest.Seiten-DOM, Bildschirmaufnahmen, Seiteninhalte, Nutzdaten von Website-API oder AI-Antworten.Klartext-IP-Adressen. Der Server verwendet die Anfrage-IP vorübergehend und inline für genau drei Zwecke – einen Hash mit täglich rotierendem Salt zur Ratenbegrenzung, die Ableitung einer groben Land-/Bundesland-Kennung (siehe unten) und die Feststellung, ob es sich um eine IPv4- oder IPv6-Adresse handelt – und verwirft sie danach sofort. Die Klartext-IP wird niemals gespeichert oder protokolliert.Namen, Benutzernamen, Konto-Handles oder beliebige Freitext-Identitätsfelder.E-Mail-Adressen, Telefonnummern oder Kontaktinformationen.Region (auf Bundeslandebene)Der Server leitet beim Eingang eine grobe Land- und US-Bundesland-Kennung (Subdivision) aus deiner Anfrage-IP ab, mithilfe eines selbst gehosteten DB-IP-IP-to-City-Lite-Datensatzes und unserer eigenen Suche – kein Live-Geolokalisierungsdienst eines Dritten und niemals MaxMind. Die Klartext-IP wird inline verarbeitet und verworfen; nur die abgeleitete Kennung und die Adressfamilie (IPv4 oder IPv6) werden behalten.Die Region wird nur aggregiert veröffentlicht, hinter einer k≥5-Anonymitätsschwelle: Jedes Bundesland mit weniger als 5 eindeutigen Installationen fällt in einen einzigen „Other“-Sammeleintrag (vollständig unterdrückt, wenn selbst diese Summe unter 5 liegt). Keine veröffentlichte Bundesland-Kennung steht jemals für weniger als 5 Installationen.Die grobe Kennung wird im Rohereignis (durch die 7-Tage-Aufbewahrung gelöscht) und im Tages-Rollup deiner Installation gespeichert, das 365 Tage lang aufbewahrt wird, damit /stats jede Installation einmal anhand ihrer jüngsten Region zählen kann. Sie ist niemals eine IP-Adresse, eine Stadt oder eine Koordinate; sie wird zusammen mit den übrigen Daten deiner Installation gelöscht, wenn du die Löschung beantragst; und sie wird nur hinter der oben beschriebenen k≥5-Schwelle veröffentlicht. Geolokalisierungsdaten von DB-IP (https://db-ip.com).AufbewahrungRohereignisse werden 7 Tage lang aufbewahrt. Tägliche Aggregate (eine Zeile pro Installation pro Tag, einschließlich der groben Regionskennung) werden 365 Tage lang aufbewahrt. Globale Aggregate (eine Zeile pro Tag, ohne Pro-Installation-Dimension) werden unbegrenzt aufbewahrt, damit historische Diagramme auf /stats stabil bleiben.Wie du dich abmeldestÖffne das FSB-Bedienfeld, scrolle zu „Erweiterte Einstellungen" und deaktiviere den Schalter Anonyme Nutzungsdaten senden. Die Änderung wird sofort wirksam; es werden keine weiteren Ereignisse von deiner Installation gesendet.Wie du deine Daten löschtUm die Löschung aller mit Ihrer Installation verknüpften Telemetriezeilen anzufordern (GDPR Artikel 17), suchen Sie Ihre fsbInstallUuid in Chrome DevTools → Anwendung → Speicher → Erweiterungsspeicher und senden Sie anschließend eine einzelne HTTP-Anfrage:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited-Use-ErklärungDie anonyme Nutzungstelemetrie von FSB wird ausschließlich verwendet, um aggregierte Nutzungsstatistiken zu berechnen, die öffentlich auf full-selfbrowsing.com/stats angezeigt werden. Die Daten werden niemals verkauft, niemals an Dritte weitergegeben, niemals für Werbung verwendet und niemals zum Training von Modellen für maschinelles Lernen verwendet. Diese Verpflichtung erfüllt die Chrome Web Store-Anforderung des Limited Use.Aggregierte öffentliche MetrikenWir veröffentlichen aggregierte Metriken aus dieser Telemetrie-Pipeline auf /stats. Es werden nur Zählungen und Summen angezeigt; niemals wird eine Zeile pro Installation offengelegt.Steuerung von Einwilligung und PrüfungFSB verfolgt bei der Automatisierung ein Opt-out-Modell: Ursprünge, die nicht auf der Sperrliste stehen, übernehmen eine vom Benutzer konfigurierbare globale Voreinstellung, die derzeit als Auto ausgeliefert wird. Explizite ursprungsspezifische Richtlinien können einen Ursprung auf Off, Ask oder Auto setzen. Das Prüfprotokoll wird lokal und zeitlich begrenzt aufbewahrt; im Kontrollzentrum stehen Funktionen zum Exportieren und Löschen bereit.Die Dienstsperrliste bleibt eine harte Blockade. Gesperrte Ursprünge können unabhängig von der globalen Voreinstellung oder einer gespeicherten ursprungsspezifischen Richtlinie nicht aktiviert werden.Auf sensiblen Ursprüngen können unter Auto Lesevorgänge ausgeführt werden; Schreibvorgänge setzen vor der Ausführung jedoch erneut die ursprungsspezifische Zustimmung zu Änderungen durch. Nicht sensible Ursprünge können deaktiviert oder im Bereich Einwilligung & Prüfung des Kontrollzentrums auf Ask gesetzt werden.Jeder Capability-Aufruf wird in einem redigierten, ausschließlich anhängenden lokalen Audit-Protokoll erfasst. Es werden niemals Argumente, Tokens, Cookies oder Antwortinhalte gespeichert.QuelloffenFSB ist unter der MIT-Lizenz vollständig quelloffen. Sie können jede Codezeile prüfen, um diese Datenschutzaussagen zu verifizieren. Der Quellcode ist auf GitHub verfügbar.Änderungen dieser RichtlinieWird diese Richtlinie aktualisiert, spiegelt sich das im Datum "Zuletzt aktualisiert" oben auf dieser Seite wider. Wesentliche Änderungen werden zusätzlich in den Release-Notes des Projekts auf GitHub vermerkt.KontaktWenn Sie Fragen zu dieser Datenschutzrichtlinie oder zum Umgang von FSB mit Daten haben, öffnen Sie bitte ein Ticket unter GitHub Issues. + + src/app/pages/privacy/privacy-history-archive.component.html + 7,238 + + August 2026v0.9.91 — Local Agent Service and Native Messaging, Delegated CLI Agents, Google Sheets, Session Replay and Screenshots (full archived text) August 2026v0.9.91 — Lokaler Agent-Dienst und Native Messaging, Delegierte CLI-Agenten, Google Sheets, Sitzungswiedergabe und Screenshots (vollständiger archivierter Text) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 242,245 @@ -9559,7 +9579,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited-Use-ErklärungDie anonyme Nutzungstelemetrie von FSB wird ausschließlich verwendet, um aggregierte Nutzungsstatistiken zu berechnen, die öffentlich auf full-selfbrowsing.com/stats angezeigt werden. Die Daten werden niemals verkauft, niemals an Dritte weitergegeben, niemals für Werbung verwendet und niemals zum Training von Modellen für maschinelles Lernen verwendet. Diese Verpflichtung erfüllt die Chrome Web Store-Anforderung des Limited Use.Aggregierte öffentliche MetrikenWir veröffentlichen aggregierte Metriken aus dieser Telemetrie-Pipeline auf /stats. Es werden nur Zählungen und Summen angezeigt; niemals wird eine Zeile pro Installation offengelegt.Steuerung von Einwilligung und PrüfungFSB verfolgt bei der Automatisierung ein Opt-out-Modell: Ursprünge, die nicht auf der Sperrliste stehen, übernehmen eine vom Benutzer konfigurierbare globale Voreinstellung, die derzeit als Auto ausgeliefert wird. Explizite ursprungsspezifische Richtlinien können einen Ursprung auf Off, Ask oder Auto setzen. Das Prüfprotokoll wird lokal und zeitlich begrenzt aufbewahrt; im Kontrollzentrum stehen Funktionen zum Exportieren und Löschen bereit.Die Dienstsperrliste bleibt eine harte Blockade. Gesperrte Ursprünge können unabhängig von der globalen Voreinstellung oder einer gespeicherten ursprungsspezifischen Richtlinie nicht aktiviert werden.Auf sensiblen Ursprüngen können unter Auto Lesevorgänge ausgeführt werden; Schreibvorgänge setzen vor der Ausführung jedoch erneut die ursprungsspezifische Zustimmung zu Änderungen durch. Nicht sensible Ursprünge können deaktiviert oder im Bereich Einwilligung & Prüfung des Kontrollzentrums auf Ask gesetzt werden.Jeder Capability-Aufruf wird in einem redigierten, ausschließlich anhängenden lokalen Audit-Protokoll erfasst. Es werden niemals Argumente, Tokens, Cookies oder Antwortinhalte gespeichert.QuelloffenFSB ist unter der MIT-Lizenz vollständig quelloffen. Sie können jede Codezeile prüfen, um diese Datenschutzaussagen zu verifizieren. Der Quellcode ist auf GitHub verfügbar.Änderungen dieser RichtlinieWird diese Richtlinie aktualisiert, spiegelt sich das im Datum "Zuletzt aktualisiert" oben auf dieser Seite wider. Wesentliche Änderungen werden zusätzlich in den Release-Notes des Projekts auf GitHub vermerkt.KontaktWenn Sie Fragen zu dieser Datenschutzrichtlinie oder zum Umgang von FSB mit Daten haben, öffnen Sie bitte ein Ticket unter GitHub Issues. src/app/pages/privacy/privacy-history-archive.component.html - 7,238 + 246,477 @@ -9567,7 +9587,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetMai – Juli 2026v0.9.90 — Site-API-Fähigkeiten, Remote Dashboard und PhantomStream, Zahlungsmethoden, Sprache-zu-Text, Regionsmetrik (vollständiger archivierter Text) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 481,484 @@ -9579,7 +9599,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetBestätigung zur eingeschränkten NutzungFSB-Telemetrie zur anonymen Nutzung dient ausschließlich der Berechnung aggregierter Nutzungsstatistiken, die öffentlich unter full-selfbrowsing.com/statsangezeigt werden. Die Daten werden niemals verkauft, an Dritte weitergegeben, für Werbung verwendet oder zum Trainieren maschineller Lernmodelle eingesetzt. Diese Verpflichtung erfüllt die Chrome Web Storeunter Limited Use .Aggregierte öffentliche KennzahlenWir veröffentlichen unter /statsaggregierte Kennzahlen aus dieser Telemetrie. Angezeigt werden nur Zahlen und Summen; installationsbezogene Zeilen werden niemals offengelegt.Steuerung von Einwilligung und PrüfungFSBverfolgt bei der Automatisierung ein Opt-out-Modell: Ursprünge, die nicht auf der Sperrliste stehen, übernehmen eine vom Benutzer konfigurierbare globale Voreinstellung, die derzeit als Auto ausgeliefert wird. Explizite ursprungsspezifische Richtlinien können einen Ursprung auf Off, Ask oder Auto setzen. Das Prüfprotokoll wird lokal und zeitlich begrenzt aufbewahrt; im Kontrollzentrum stehen Funktionen zum Exportieren und Löschen bereit.Die Dienstsperrliste bleibt eine harte Blockade. Gesperrte Ursprünge können unabhängig von der globalen Voreinstellung oder einer gespeicherten ursprungsspezifischen Richtlinie nicht aktiviert werden.Auf sensiblen Ursprüngen können unter Auto Lesevorgänge ausgeführt werden; Schreibvorgänge setzen vor der Ausführung jedoch erneut die ursprungsspezifische Zustimmung zu Änderungen durch. Nicht sensible Ursprünge können deaktiviert oder im Bereich Einwilligung & Prüfung des Kontrollzentrums auf Ask gesetzt werden.Jeder Fähigkeitsaufruf wird in einem geschwärzten, nur ergänzbaren lokalen Prüfprotokoll aufgezeichnet. Argumente, Token, Cookies oder Antworttexte werden niemals gespeichert.QuelloffenFSB ist unter der MIT -Lizenz vollständig quelloffen. Sie können jede Codezeile prüfen, um diese Datenschutzaussagen zu verifizieren. Der Quellcode ist auf GitHub.Änderungen an dieser RichtlinieWenn diese Richtlinie aktualisiert wird, zeigt das Datum „Zuletzt aktualisiert“ am Seitenanfang die Änderung. Wesentliche Änderungen werden außerdem in den GitHub -Versionshinweisen des Projekts vermerkt.KontaktWenn Sie Fragen zu dieser Datenschutzrichtlinie oder zum Umgang von FSBmit Daten haben, öffnen Sie bitte ein Ticket unter GitHub Issues. src/app/pages/privacy/privacy-history-archive.component.html - 7,198 + 485,676 @@ -9587,7 +9607,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetMärz 2026v9.0.2 — Background Agents, Gedächtnissystem, Serversynchronisierung, anonyme Nutzungstelemetrie, Rechtslage (vollständiger archivierter Text) src/app/pages/privacy/privacy-history-archive.component.html - 202,205 + 680,683 @@ -9599,7 +9619,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetBestätigung zur eingeschränkten NutzungFSB-Telemetrie zur anonymen Nutzung dient ausschließlich der Berechnung aggregierter Nutzungsstatistiken, die öffentlich unter full-selfbrowsing.com/stats angezeigt werden. Die Daten werden niemals verkauft, an Dritte weitergegeben, für Werbung verwendet oder zum Trainieren maschineller Lernmodelle eingesetzt. Diese Verpflichtung erfüllt die Chrome Web Store-Anforderung Limited Use.Aggregierte öffentliche KennzahlenWir veröffentlichen unter /stats aggregierte Kennzahlen aus dieser Telemetrie. Angezeigt werden nur Zahlen und Summen; installationsbezogene Zeilen werden niemals offengelegt.Rechtslage und EinwilligungsmodellFSB-Automatisierungsmodell, die Aufbewahrung von Prüfprotokollen und das ursprungsspezifische Einwilligungsmodell wurden als ausdrückliche Produktsteuerungen dargestellt. FSB führt auf einem Ursprung nichts aus, bis Sie es ausdrücklich erlauben. Automatisierter Lesezugriff beinhaltet niemals Schreibzugriff, und eine konservative Dienstsperrliste blockiert Automatisierung auf sensiblen Kategorien (Finanz- und Behördendienste) vollständig.Die ursprungsspezifische Einwilligung ist standardmäßig deaktiviert. Auto- und Schreibzugriff (mit Änderungen) sind getrennte, ausdrückliche Zustimmungen, die im Bereich Einwilligung & Prüfung des Kontrollzentrums verwaltet werden.Jeder Fähigkeitsaufruf wird in einem geschwärzten, nur ergänzbaren lokalen Prüfprotokoll aufgezeichnet. Argumente, Token, Cookies oder Antworttexte werden niemals gespeichert.Eine Dienstsperrliste macht sensible Ursprünge nicht aktivierbar; dies wird an der Fähigkeitsschranke und nicht nur in der Oberfläche durchgesetzt.QuelloffenFSB ist unter der MIT-Lizenz vollständig quelloffen. Sie können jede Codezeile prüfen, um diese Datenschutzaussagen zu verifizieren. Der Quellcode ist auf GitHub verfügbar.Änderungen an dieser RichtlinieWenn diese Richtlinie aktualisiert wird, zeigt das Datum „Zuletzt aktualisiert“ am Seitenanfang die Änderung. Wesentliche Änderungen werden außerdem in den GitHub-Versionshinweisen des Projekts vermerkt.KontaktWenn Sie Fragen zu dieser Datenschutzrichtlinie oder zum Umgang von FSB mit Daten haben, öffnen Sie bitte ein Ticket unter GitHub Issues. src/app/pages/privacy/privacy-history-archive.component.html - 206,345 + 684,823 @@ -9607,7 +9627,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFebruar 2026v0.9 — Ursprüngliche Datenschutzrichtlinie (vollständiger archivierter Text) src/app/pages/privacy/privacy-history-archive.component.html - 349,352 + 827,830 @@ -9615,7 +9635,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetArchivierte Fassung der ursprünglichen Datenschutzrichtlinie vom Februar 2026. Diese Momentaufnahme wurde von der ursprünglichen statischen Präsentationsseite übernommen; die Seitenoberfläche wurde entfernt und der Richtlinientext bewahrt.DatenerfassungFSB wird vollständig in Ihrem Browser ausgeführt. Die Erweiterung greift nur auf das DOM (Dokumentenobjektmodell) des aktuell aktiven Tabs zu, wenn Sie eine Automatisierungsaufgabe starten.Über die aktuelle Sitzung hinaus wird kein Browserverlauf erfasst oder gespeichertDOM-Daten werden lokal analysiert und nach jedem Automatisierungsschritt verworfenVon den von Ihnen besuchten Seiten werden keine personenbezogenen Daten gesammeltDatenspeicherungAlle Einstellungen und Daten werden lokal im Erweiterungsspeicher von Chrome gespeichert. FSB verwendet AES-GCM-Verschlüsselung für sensible Daten wie API-Schlüssel.Die Konfiguration wird in chrome.storage.localAPI-Schlüssel werden vor der Speicherung mit AES-GCMSitzungsprotokolle werden lokal gespeichert und können jederzeit gelöscht werdenAnalysedaten (Aufgabenzahlen, Erfolgsquoten) bleiben auf Ihrem GerätExterne DiensteFSB kommuniziert nur dann mit externen KI-Anbietern, wenn Sie sie konfigurieren und verwenden. Sie bestimmen, welcher Anbieter verwendet wird und welche Daten gesendet werden.API-Aufrufe erfolgen nur an den von Ihnen ausgewählten Anbieter (xAI, OpenAI, Anthropic, oder Google)Gesendete Daten umfassen: Aufgabenbeschreibung, DOM-Strukturzusammenfassung und AktionskontextEs werden keine Daten an FSB-Server gesendet – solche Server gibt es nichtJeder Anbieter besitzt eine eigene Datenschutzrichtlinie für den Umgang mit API-AnfragenKein TrackingFSB enthält keine Analyse-, Telemetrie- oder Trackingdienste. Es gibt keine Cookies, kein Fingerprinting und keine Drittanbieterskripte außer den ausdrücklich von Ihnen konfigurierten APIs der KI-Anbieter.API-SchlüsselIhre API-Schlüssel werden vor der Speicherung lokal mit AES-GCM verschlüsselt. Sie werden ausschließlich an den von Ihnen konfigurierten KI-Anbieter und nur als Authentifizierungsheader in API-Anfragen übertragen.Ruhende Schlüssel werden verschlüsselt im Chrome-Speicher abgelegtDie Entschlüsselung erfolgt bei API-Aufrufen nur im ArbeitsspeicherSchlüssel werden niemals protokolliert, exportiert oder weitergegebenAuto-Passwörter BetaFSB enthält eine optionale Anmeldedatenverwaltung, die Anmeldedaten verschlüsselt auf Ihrem Gerät speichert. Passwörter werden niemals KI-Modellen offengelegt – das Inhaltsskript trägt sie direkt in Seiten ein und umgeht die KI vollständig.Ruhende Anmeldedaten werden mit AES-GCM und 256-Bit-Schlüsseln sowie PBKDF2-Schlüsselableitung verschlüsseltWenn die KI eine Seite analysiert, werden Werte von Passwortfeldern durch [hidden] – das tatsächliche Passwort ist niemals in einer KI-Anweisung enthaltenDas Inhaltsskript füllt Werte automatisch aus, indem es sie direkt in das DOM einfügt; die KI ist nicht am Anmeldedatenfluss beteiligtDie Anmeldedatenliste zeigt nur Benutzernamen und Domänen – Passwörter werden einzeln und nur bei Bedarf zum automatischen Ausfüllen entschlüsseltAnmeldedaten werden pro Domäne mit Rückfall auf die übergeordnete Domäne gespeichert (z. B. accounts.google.com übernimmt von google.com)Schutz vor Prompt-InjectionWebseiten können ausgeblendeten Text enthalten, der KI-Agenten übernehmen soll. FSB setzt mehrschichtige Schutzmaßnahmen ein, damit die KI nur Ihren Anweisungen folgt und niemals Anweisungen aus Seiteninhalten.Alle Seiteninhalte werden in [PAGE_CONTENT]-Grenzmarkierungen eingeschlossen, und die KI wird angewiesen, Anweisungen innerhalb dieser Markierungen niemals zu befolgenEine Bereinigungs-Engine entfernt bekannte Injektionsmuster (z. B. „vorherige Anweisungen ignorieren“, gefälschte Systemanweisungen, Überschreibungsversuche) aus allen Seiteninhalten, bevor diese die KI erreichenVon der KI erzeugte Aktionen werden vor der Ausführung validiert – gefährliche URLs (javascript:, data:) und Versuche zur Skriptinjektion werden blockiertNur eine strenge Positivliste mit mehr als 30 bekannten Werkzeugen kann ausgeführt werden – die KI kann keine beliebigen Aktionen erfinden oder aufrufenDie Inhaltsgröße ist begrenzt (500 Zeichen pro Wert, insgesamt 15.000 Zeichen pro Anweisung), um die Übertragung von Nutzdaten einzuschränkenUnsichtbare Unicode-Steuerzeichen, die Websites einbetten, werden vor der Verarbeitung entferntQuelloffenFSB ist unter der MIT-Lizenz vollständig quelloffen. Sie können jede Codezeile prüfen, um diese Datenschutzaussagen zu verifizieren. Der Quellcode ist auf GitHub verfügbar.Quellcode auf GitHub ansehenZusammenfassungFSB verarbeitet Daten lokal, verschlüsselt sensible Informationen, legt Passwörter niemals KI-Modellen offen, schützt vor Prompt-Injection-Angriffen, kommuniziert nur mit den von Ihnen gewählten KI-Anbietern, enthält kein Tracking und ist als quelloffene Software vollständig überprüfbar. src/app/pages/privacy/privacy-history-archive.component.html - 353,420 + 831,898 @@ -9635,8 +9655,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Last updated: September 2026 - Zuletzt aktualisiert: September 2026 + Last updated: September 29, 2026 + Zuletzt aktualisiert: 29. September 2026 src/app/pages/privacy/privacy-page.component.html 6,9 @@ -10563,8 +10583,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. - FSB v0.9.69 hat eine opt-out-fähige Pipeline für anonyme Nutzungstelemetrie eingeführt, damit das Projekt aggregierte Adoptionszahlen veröffentlichen kann (siehe /stats), ohne jemals auf die von dir besuchten Seiten zuzugreifen. Das einzige Standortsignal ist eine grobe Land-/Bundesland-Kennung, die der Server beim Eingang aus deiner Anfrage-IP ableitet (niemals aus Seiteninhalten) und nur aggregiert veröffentlicht – siehe Region (auf Bundeslandebene) weiter unten. Telemetrie ist standardmäßig aktiv, kann aber mit einem einzigen Schalter deaktiviert werden, und die Daten pro Installation können auf Anfrage gelöscht werden. + FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse place label — city, state or province, and country — that the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (city-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. + FSB v0.9.69 hat eine opt-out-fähige Pipeline für anonyme Nutzungstelemetrie eingeführt, damit das Projekt aggregierte Adoptionszahlen veröffentlichen kann (siehe /stats), ohne jemals auf die von dir besuchten Seiten zuzugreifen. Das einzige Standortsignal ist eine grobe Ortskennung – Stadt, Bundesland oder Provinz und Land –, die der Server beim Eingang aus deiner Anfrage-IP ableitet (niemals aus Seiteninhalten) und nur aggregiert veröffentlicht – siehe Region (auf Stadtebene) weiter unten. Telemetrie ist standardmäßig aktiv, kann aber mit einem einzigen Schalter deaktiviert werden, und die Daten pro Installation können auf Anfrage gelöscht werden. src/app/pages/privacy/privacy-page.component.html 197,199 @@ -10651,8 +10671,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. - Klartext-IP-Adressen. Der Server verwendet die Anfrage-IP vorübergehend und inline für genau drei Zwecke – einen Hash mit täglich rotierendem Salt zur Ratenbegrenzung, die Ableitung einer groben Land-/Bundesland-Kennung (siehe unten) und die Feststellung, ob es sich um eine IPv4- oder IPv6-Adresse handelt – und verwirft sie danach sofort. Die Klartext-IP wird niemals gespeichert oder protokolliert. + Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse city, state, and country label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. + Klartext-IP-Adressen. Der Server verwendet die Anfrage-IP vorübergehend und inline für genau drei Zwecke – einen Hash mit täglich rotierendem Salt zur Ratenbegrenzung, die Ableitung einer groben Stadt-, Bundesland- und Landeskennung (siehe unten) und die Feststellung, ob es sich um eine IPv4- oder IPv6-Adresse handelt – und verwirft sie danach sofort. Die Klartext-IP wird niemals gespeichert oder protokolliert. src/app/pages/privacy/privacy-page.component.html 213,214 @@ -10675,43 +10695,51 @@ https://full-selfbrowsing.com/api/telemetry/forget - Region (state-level) metric - Region (auf Bundeslandebene) + Region (city-level) metric + Region (auf Stadtebene) src/app/pages/privacy/privacy-page.component.html 218,220 - The server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. - Der Server leitet beim Eingang eine grobe Land- und US-Bundesland-Kennung (Subdivision) aus deiner Anfrage-IP ab, mithilfe eines selbst gehosteten DB-IP-IP-to-City-Lite-Datensatzes und unserer eigenen Suche – kein Live-Geolokalisierungsdienst eines Dritten und niemals MaxMind. Die Klartext-IP wird inline verarbeitet und verworfen; nur die abgeleitete Kennung und die Adressfamilie (IPv4 oder IPv6) werden behalten. + The server derives a coarse place label — country, state or province, and city — from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. + Der Server leitet beim Eingang eine grobe Ortskennung – Land, Bundesland oder Provinz und Stadt – aus deiner Anfrage-IP ab, mithilfe eines selbst gehosteten DB-IP-IP-to-City-Lite-Datensatzes und unserer eigenen Suche – kein Live-Geolokalisierungsdienst eines Dritten und niemals MaxMind. Die Klartext-IP wird inline verarbeitet und verworfen; nur die abgeleitete Kennung und die Adressfamilie (IPv4 oder IPv6) werden behalten. src/app/pages/privacy/privacy-page.component.html 220,221 - Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs. - Die Region wird nur aggregiert veröffentlicht, hinter einer k≥5-Anonymitätsschwelle: Jedes Bundesland mit weniger als 5 eindeutigen Installationen fällt in einen einzigen „Other“-Sammeleintrag (vollständig unterdrückt, wenn selbst diese Summe unter 5 liegt). Keine veröffentlichte Bundesland-Kennung steht jemals für weniger als 5 Installationen. + Region is published only in aggregate, behind a k≥5 anonymity floor applied level by level: an install is counted under its city when at least 5 distinct installs share that city, otherwise under its state or province, then its country, by the same rule. Anything still below 5 collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). Each install is counted in exactly one bucket, and no published city, state, or country label ever represents fewer than 5 installs. + Die Region wird nur aggregiert veröffentlicht, hinter einer k≥5-Anonymitätsschwelle, die Ebene für Ebene angewendet wird: Eine Installation wird unter ihrer Stadt gezählt, wenn mindestens 5 eindeutige Installationen diese Stadt teilen, andernfalls nach derselben Regel unter ihrem Bundesland bzw. ihrer Provinz und danach unter ihrem Land. Alles, was dann noch unter 5 liegt, fällt in einen einzigen „Other“-Sammeleintrag (vollständig unterdrückt, wenn selbst diese Summe unter 5 liegt). Jede Installation wird in genau einem Eintrag gezählt, und keine veröffentlichte Stadt-, Bundesland- oder Landeskennung steht jemals für weniger als 5 Installationen. src/app/pages/privacy/privacy-page.component.html 221 - - The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). - Die grobe Kennung wird im Rohereignis (durch die 7-Tage-Aufbewahrung gelöscht) und im Tages-Rollup deiner Installation gespeichert, das 365 Tage lang aufbewahrt wird, damit /stats jede Installation einmal anhand ihrer jüngsten Region zählen kann. Sie ist niemals eine IP-Adresse, eine Stadt oder eine Koordinate; sie wird zusammen mit den übrigen Daten deiner Installation gelöscht, wenn du die Löschung beantragst; und sie wird nur hinter der oben beschriebenen k≥5-Schwelle veröffentlicht. Geolokalisierungsdaten von DB-IP (https://db-ip.com). + + The /stats globe places each published label at the approximate center of that city, state, or country, taken from the same dataset — never at the location of any install. + Der Globus auf /stats platziert jede veröffentlichte Kennung am ungefähren Mittelpunkt der jeweiligen Stadt, des Bundeslands oder des Landes, entnommen aus demselben Datensatz – niemals am Standort einer Installation. src/app/pages/privacy/privacy-page.component.html 222,223 + + The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, street address, or coordinates — a city name is the finest detail kept; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). + Die grobe Kennung wird im Rohereignis (durch die 7-Tage-Aufbewahrung gelöscht) und im Tages-Rollup deiner Installation gespeichert, das 365 Tage lang aufbewahrt wird, damit /stats jede Installation einmal anhand ihrer jüngsten Region zählen kann. Sie ist niemals eine IP-Adresse, eine Straßenanschrift oder eine Koordinate – ein Stadtname ist das feinste gespeicherte Detail; sie wird zusammen mit den übrigen Daten deiner Installation gelöscht, wenn du die Löschung beantragst; und sie wird nur hinter der oben beschriebenen k≥5-Schwelle veröffentlicht. Geolokalisierungsdaten von DB-IP (https://db-ip.com). + + src/app/pages/privacy/privacy-page.component.html + 223,224 + + Retention Aufbewahrung src/app/pages/privacy/privacy-page.component.html - 225,226 + 226,227 @@ -10719,7 +10747,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetRohereignisse werden 7 Tage lang aufbewahrt. Tägliche Aggregate (eine Zeile pro Installation pro Tag, einschließlich der groben Regionskennung) werden 365 Tage lang aufbewahrt. Globale Aggregate (eine Zeile pro Tag, ohne Pro-Installation-Dimension) werden unbegrenzt aufbewahrt, damit historische Diagramme auf /stats stabil bleiben. src/app/pages/privacy/privacy-page.component.html - 226,228 + 227,229 @@ -10727,7 +10755,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetWie du dich abmeldest src/app/pages/privacy/privacy-page.component.html - 228,229 + 229,230 @@ -10735,7 +10763,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetÖffne das FSB-Bedienfeld, scrolle zu „Erweiterte Einstellungen" und deaktiviere den Schalter Anonyme Nutzungsdaten senden. Die Änderung wird sofort wirksam; es werden keine weiteren Ereignisse von deiner Installation gesendet. src/app/pages/privacy/privacy-page.component.html - 229,231 + 230,232 @@ -10743,7 +10771,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetWie du deine Daten löscht src/app/pages/privacy/privacy-page.component.html - 231,232 + 232,233 @@ -10751,7 +10779,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetUm die Löschung aller mit Ihrer Installation verknüpften Telemetriezeilen anzufordern (GDPR Artikel 17), suchen Sie Ihre fsbInstallUuid in Chrome DevTools → Anwendung → Speicher → Erweiterungsspeicher und senden Sie anschließend eine einzelne HTTP-Anfrage: src/app/pages/privacy/privacy-page.component.html - 232,233 + 233,234 @@ -10763,7 +10791,7 @@ https://full-selfbrowsing.com/api/telemetry/forget src/app/pages/privacy/privacy-page.component.html - 233,237 + 234,238 @@ -10771,7 +10799,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited-Use-Erklärung src/app/pages/privacy/privacy-page.component.html - 237,238 + 238,239 @@ -10779,7 +10807,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetDie anonyme Nutzungstelemetrie von FSB wird ausschließlich verwendet, um aggregierte Nutzungsstatistiken zu berechnen, die öffentlich auf full-selfbrowsing.com/stats angezeigt werden. Die Daten werden niemals verkauft, niemals an Dritte weitergegeben, niemals für Werbung verwendet und niemals zum Training von Modellen für maschinelles Lernen verwendet. Diese Verpflichtung erfüllt die Chrome Web Store-Anforderung des Limited Use. src/app/pages/privacy/privacy-page.component.html - 238,240 + 239,241 @@ -10787,7 +10815,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetAggregierte öffentliche Metriken src/app/pages/privacy/privacy-page.component.html - 240,241 + 241,242 @@ -10795,7 +10823,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetWir veröffentlichen aggregierte Metriken aus dieser Telemetrie-Pipeline auf /stats. Es werden nur Zählungen und Summen angezeigt; niemals wird eine Zeile pro Installation offengelegt. src/app/pages/privacy/privacy-page.component.html - 241,243 + 242,244 @@ -10803,7 +10831,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetSteuerung von Einwilligung und Prüfung src/app/pages/privacy/privacy-page.component.html - 244,245 + 245,246 @@ -10811,7 +10839,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB verfolgt bei der Automatisierung ein Opt-out-Modell: Ursprünge, die nicht auf der Sperrliste stehen, übernehmen eine vom Benutzer konfigurierbare globale Voreinstellung, die derzeit als Auto ausgeliefert wird. Explizite ursprungsspezifische Richtlinien können einen Ursprung auf Off, Ask oder Auto setzen. Das Prüfprotokoll wird lokal und zeitlich begrenzt aufbewahrt; im Kontrollzentrum stehen Funktionen zum Exportieren und Löschen bereit. src/app/pages/privacy/privacy-page.component.html - 245,247 + 246,248 @@ -10819,7 +10847,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetDie Dienstsperrliste bleibt eine harte Blockade. Gesperrte Ursprünge können unabhängig von der globalen Voreinstellung oder einer gespeicherten ursprungsspezifischen Richtlinie nicht aktiviert werden. src/app/pages/privacy/privacy-page.component.html - 247,248 + 248,249 @@ -10827,7 +10855,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetAuf sensiblen Ursprüngen können unter Auto Lesevorgänge ausgeführt werden; Schreibvorgänge setzen vor der Ausführung jedoch erneut die ursprungsspezifische Zustimmung zu Änderungen durch. Nicht sensible Ursprünge können deaktiviert oder im Bereich Einwilligung & Prüfung des Kontrollzentrums auf Ask gesetzt werden. src/app/pages/privacy/privacy-page.component.html - 248,249 + 249,250 @@ -10835,7 +10863,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetJeder Capability-Aufruf wird in einem redigierten, ausschließlich anhängenden lokalen Audit-Protokoll erfasst. Es werden niemals Argumente, Tokens, Cookies oder Antwortinhalte gespeichert. src/app/pages/privacy/privacy-page.component.html - 249,252 + 250,253 @@ -10843,7 +10871,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetQuelloffen src/app/pages/privacy/privacy-page.component.html - 253,254 + 254,255 @@ -10851,7 +10879,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB ist unter der MIT-Lizenz vollständig quelloffen. Sie können jede Codezeile prüfen, um diese Datenschutzaussagen zu verifizieren. Der Quellcode ist auf GitHub verfügbar. src/app/pages/privacy/privacy-page.component.html - 254,256 + 255,257 @@ -10859,7 +10887,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetÄnderungen dieser Richtlinie src/app/pages/privacy/privacy-page.component.html - 257,258 + 258,259 @@ -10867,7 +10895,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetWird diese Richtlinie aktualisiert, spiegelt sich das im Datum "Zuletzt aktualisiert" oben auf dieser Seite wider. Wesentliche Änderungen werden zusätzlich in den Release-Notes des Projekts auf GitHub vermerkt. src/app/pages/privacy/privacy-page.component.html - 258,260 + 259,261 @@ -10875,7 +10903,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetKontakt src/app/pages/privacy/privacy-page.component.html - 261,262 + 262,263 @@ -10883,7 +10911,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetWenn Sie Fragen zu dieser Datenschutzrichtlinie oder zum Umgang von FSB mit Daten haben, öffnen Sie bitte ein Ticket unter GitHub Issues. src/app/pages/privacy/privacy-page.component.html - 262,264 + 263,265 @@ -10891,7 +10919,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetVersionsverlauf der Richtlinie src/app/pages/privacy/privacy-page.component.html - 265,266 + 266,267 @@ -10899,7 +10927,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetJeder der folgenden Einträge ist eine vollständige Momentaufnahme der Datenschutzrichtlinie zum angegebenen Datum. Ältere Versionen bleiben erhalten, damit Sie jederzeit nachvollziehen können, was wir zugesagt haben. src/app/pages/privacy/privacy-page.component.html - 266,268 + 267,269 @@ -11623,7 +11651,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetsteuert den Browser src/app/pages/release-notes/release-notes-page.component.html - 176,180 + 176,179 @@ -11775,7 +11803,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetEingabe gesperrt · eine andere Automatisierung besitzt diesen Tab src/app/pages/release-notes/release-notes-page.component.html - 226,231 + 226,230 @@ -12351,7 +12379,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetEinzelelement src/app/pages/release-notes/release-notes-page.component.html - 408,412 + 408,411 @@ -12511,7 +12539,7 @@ https://full-selfbrowsing.com/api/telemetry/forget560 abgesichert src/app/pages/release-notes/release-notes-page.component.html - 469,473 + 469,472 @@ -12671,7 +12699,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetnode 24 src/app/pages/release-notes/release-notes-page.component.html - 529,533 + 529,532 @@ -17999,7 +18027,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB - Versionshinweise src/app/pages/release-notes/release-notes-page.component.ts - 88 + 86 @@ -18007,7 +18035,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetJede Version von FSB, bebildert: delegierte Agentenlaufzeiten, das Aktivitätssymbol in der Symbolleiste, Tab-Besitz für mehrere Agenten, die MCP-Brücke und der Fähigkeitskatalog, zurück bis zum ersten Prototyp der Chrome-Erweiterung. src/app/pages/release-notes/release-notes-page.component.ts - 89 + 87 @@ -18179,11 +18207,11 @@ https://full-selfbrowsing.com/api/telemetry/forget - Where installs were last seen, by coarse region, over the past 365 days. - Wo Installationen zuletzt gesehen wurden, nach grober Region, über die letzten 365 Tage. + Where installs were last seen over the past 365 days, by city, state, or country. Place data by DB-IP. + Wo Installationen in den letzten 365 Tagen zuletzt gesehen wurden, nach Stadt, Bundesland oder Land. Ortsdaten von DB-IP. src/app/pages/stats/stats-page.component.html - 104,106 + 104,105 @@ -18239,7 +18267,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetNetzwerk- oder Analysefehler. src/app/pages/stats/stats-page.component.ts - 132 + 133 @@ -18247,7 +18275,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetSterne src/app/pages/stats/stats-page.component.ts - 139 + 140 @@ -18255,7 +18283,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetÄnderungen src/app/pages/stats/stats-page.component.ts - 140 + 141 @@ -18263,7 +18291,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetJetzt aktiv src/app/pages/stats/stats-page.component.ts - 141 + 142 @@ -18271,7 +18299,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetToken src/app/pages/stats/stats-page.component.ts - 142 + 143 @@ -18279,27 +18307,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetBeliebt src/app/pages/stats/stats-page.component.ts - 143 - - - - Could not load chart library. - Die Diagrammbibliothek konnte nicht geladen werden. - - src/app/pages/stats/stats-page.component.ts - 183 - - - - Could not render the selected chart. - Das ausgewählte Diagramm konnte nicht dargestellt werden. - - src/app/pages/stats/stats-page.component.ts - 190 - - - src/app/pages/stats/stats-page.component.ts - 862 + 144 @@ -18307,7 +18315,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetDie Statistiken werden aufgewärmt; es wird in Kürze erneut versucht. src/app/pages/stats/stats-page.component.ts - 232 + 233 @@ -18315,7 +18323,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetIn der Statistikantwort fehlen die Aktualitäts-Metadaten. src/app/pages/stats/stats-page.component.ts - 235 + 236 @@ -18323,7 +18331,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetDie letzte verwendbare Momentaufnahme ist mehr als 24 Stunden alt. src/app/pages/stats/stats-page.component.ts - 238 + 239 @@ -18331,7 +18339,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetDie Statistikantwort war fehlerhaft. src/app/pages/stats/stats-page.component.ts - 241 + 242 @@ -18339,7 +18347,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetStatistiken sind nur im Browser verfügbar. src/app/pages/stats/stats-page.component.ts - 244 + 245 @@ -18347,7 +18355,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetKumulierte Repository-Sterne im Zeitverlauf src/app/pages/stats/stats-page.component.ts - 252 + 253 @@ -18355,7 +18363,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetKumulierte Repository-Commits im Zeitverlauf src/app/pages/stats/stats-page.component.ts - 254 + 255 @@ -18363,7 +18371,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB-Token-Nutzung in den letzten 30 Tagen src/app/pages/stats/stats-page.component.ts - 256 + 257 @@ -18371,7 +18379,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetAnteil der erfassten MCP-Clients src/app/pages/stats/stats-page.component.ts - 258 + 259 @@ -18379,7 +18387,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetUnbekannt src/app/pages/stats/stats-page.component.ts - 295 + 296 @@ -18387,15 +18395,15 @@ https://full-selfbrowsing.com/api/telemetry/forgetSonstige src/app/pages/stats/stats-page.component.ts - 296 + 297 - Globe showing where FSB installs were last seen over the past 365 days, by coarse region - Globus, der zeigt, wo FSB-Installationen in den letzten 365 Tagen zuletzt gesehen wurden, nach grober Region + Globe showing where FSB installs were last seen over the past 365 days, by city, state, or country + Globus, der zeigt, wo FSB-Installationen in den letzten 365 Tagen zuletzt gesehen wurden, nach Stadt, Bundesland oder Land src/app/pages/stats/stats-page.component.ts - 308 + 309 @@ -18403,7 +18411,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetSterne insgesamt src/app/pages/stats/stats-page.component.ts - 317 + 318 @@ -18411,7 +18419,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetletzte 7 Tage src/app/pages/stats/stats-page.component.ts - 318 + 319 @@ -18419,7 +18427,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetCommits insgesamt src/app/pages/stats/stats-page.component.ts - 325 + 326 @@ -18427,7 +18435,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetletzte 30 Tage src/app/pages/stats/stats-page.component.ts - 329 + 330 @@ -18435,7 +18443,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetaktive Agenten src/app/pages/stats/stats-page.component.ts - 343 + 344 @@ -18443,7 +18451,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetDurchschnitt/meldender Nutzer src/app/pages/stats/stats-page.component.ts - 347 + 348 @@ -18451,7 +18459,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetToken src/app/pages/stats/stats-page.component.ts - 357 + 358 @@ -18459,7 +18467,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetToken (24 Std.) src/app/pages/stats/stats-page.component.ts - 358 + 359 @@ -18467,7 +18475,7 @@ https://full-selfbrowsing.com/api/telemetry/forgeterfasste Clients src/app/pages/stats/stats-page.component.ts - 364 + 365 @@ -18475,7 +18483,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetSpitzenreiter: src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18483,7 +18491,7 @@ https://full-selfbrowsing.com/api/telemetry/forgethäufigster Client src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18491,7 +18499,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetStatistikansicht auswählen. Aktuelle Ansicht: src/app/pages/stats/stats-page.component.ts - 402 + 403 @@ -18499,7 +18507,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB · Statistiken src/app/pages/stats/stats-page.component.ts - 478 + 484 @@ -18507,7 +18515,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetAggregierte Live-Daten zur Verbreitung und Nutzung von FSB sowie zu Repository-Aktivitäten. src/app/pages/stats/stats-page.component.ts - 486 + 492 @@ -18515,7 +18523,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetKumulative Sterne src/app/pages/stats/stats-page.component.ts - 974 + 979 @@ -18523,7 +18531,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetKumulative Commits src/app/pages/stats/stats-page.component.ts - 995 + 1000 @@ -18531,7 +18539,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetToken (letzte 30 Tage) src/app/pages/stats/stats-page.component.ts - 1023 + 1028 @@ -18539,7 +18547,23 @@ https://full-selfbrowsing.com/api/telemetry/forgetBeliebte MCP-Clients src/app/pages/stats/stats-page.component.ts - 1046 + 1051 + + + + Could not load chart library. + Die Diagrammbibliothek konnte nicht geladen werden. + + src/app/pages/stats/stats-page.component.ts + 1105 + + + + Could not render the selected chart. + Das ausgewählte Diagramm konnte nicht dargestellt werden. + + src/app/pages/stats/stats-page.component.ts + 1106 diff --git a/showcase/angular/src/locale/messages.es.xlf b/showcase/angular/src/locale/messages.es.xlf index 15751644..43ae31c6 100644 --- a/showcase/angular/src/locale/messages.es.xlf +++ b/showcase/angular/src/locale/messages.es.xlf @@ -5515,7 +5515,7 @@ Última instantánea: src/app/pages/dashboard/dashboard-page.component.ts - 3994 + 4014 @@ -5523,7 +5523,7 @@ Estado: src/app/pages/dashboard/dashboard-page.component.ts - 3998 + 4018 @@ -5531,7 +5531,7 @@ Motivo: src/app/pages/dashboard/dashboard-page.component.ts - 4002 + 4022 @@ -5539,7 +5539,7 @@ Recuperando desde hace s src/app/pages/dashboard/dashboard-page.component.ts - 4006 + 4026 @@ -5547,7 +5547,7 @@ último fotograma: hace s src/app/pages/dashboard/dashboard-page.component.ts - 4012 + 4032 @@ -5555,7 +5555,7 @@ mutaciones: src/app/pages/dashboard/dashboard-page.component.ts - 4013 + 4033 @@ -5563,7 +5563,7 @@ fallos de aplicación: src/app/pages/dashboard/dashboard-page.component.ts - 4014 + 4034 @@ -5571,7 +5571,7 @@ obsoleto: src/app/pages/dashboard/dashboard-page.component.ts - 4015 + 4035 @@ -5579,7 +5579,7 @@ No hay datos de transmisión src/app/pages/dashboard/dashboard-page.component.ts - 4016 + 4036 @@ -5587,7 +5587,7 @@ La solicitud falló con el estado src/app/pages/dashboard/dashboard-page.component.ts - 4319 + 4339 @@ -5595,7 +5595,7 @@ ~ min restantes src/app/pages/dashboard/dashboard-page.component.ts - 4474 + 4494 @@ -5603,7 +5603,7 @@ ~ s restantes src/app/pages/dashboard/dashboard-page.component.ts - 4475 + 4495 @@ -5611,7 +5611,7 @@ En ejecución durante src/app/pages/dashboard/dashboard-page.component.ts - 4549 + 4569 @@ -5619,7 +5619,7 @@ Detenida por el usuario; antes: src/app/pages/dashboard/dashboard-page.component.ts - 4554 + 4574 @@ -9542,12 +9542,32 @@ 307 + + September 2026v0.9.91 — 365-day region retention, Region (state-level) metric (full archived text) + Septiembre de 2026v0.9.91 — Retención de la región durante 365 días, Métrica de región (a nivel de estado) (texto archivado completo) + + src/app/pages/privacy/privacy-history-archive.component.html + 3,6 + + + + Archived copy of the privacy policy as it stood from September 28, 2026, before the September 29, 2026 update.TLDR: FSB runs inside your browser, and no browsing data is collected by FSB servers. AI calls go directly from your browser to the provider you choose. API keys and credentials are encrypted locally, and memory data stays on your device. If you install the optional local agent service, FSB can also be driven by an MCP client running on your own machine over a loopback connection, and by coding-agent CLIs you have already installed. The only data that ever leaves your machine for FSB is opt-out anonymous usage telemetry that powers the public /stats page. If you pair Remote Dashboard, transient live-preview frames can pass through the relay during the session, but they are not stored. Everything is open source and auditable.Data CollectionFSB operates entirely within your browser. When you initiate an automation task, the extension may inspect the active tab's DOM (Document Object Model) and, when you invoke a capability, make same-origin site API requests from your browser session.No browsing history is collected or stored beyond the current sessionDOM data and site API results are analyzed locally and discarded after each automation step unless you explicitly save them in memoryNo personal information is harvested from pages you visitSite API capabilitiesFSB's capability layer can call a site's own first-party API from the page context, using your already-authenticated browser session. These requests run locally in Chrome; first-party cookies or site auth may be attached by the browser for that target site, but FSB does not return, store, log, or send cookies, tokens, CSRF values, request bodies, or response bodies to FSB servers.Capability invocations follow FSB's consent controls and are recorded only in the redacted local audit log: origin, capability slug, method, side-effect class, consent decision, and outcome. The audit log never stores invocation arguments, request bodies, response bodies, cookies, tokens, CSRF values, or site response payloads.Chrome permissions FSB requestsTo run web automation, FSB declares the following permissions in its Chrome manifest. Each is used only for the documented purpose; nothing is sent off-device on the strength of any of them.DOM and tabs, activeTab, scripting, tabs, windows, sidePanel, and host permission <all_urls>: read and write the active tab, inject the automation content script, list and switch tabs, and render the side panelAdvanced automation, debugger: attach the Chrome DevTools Protocol for coordinate-based clicks, drag, and key-hold actions that the regular DOM API cannot perform. webNavigation: observe navigation start/finish events so automation waits for the right momentLocal storage, storage, unlimitedStorage: store your settings, credentials, payment methods, and memory in chrome.storage.local on your device. Unlimited storage lifts the default 10 MB quota so memory and session logs can grow without hitting a wallUX helpers, clipboardWrite: write copy-to-clipboard results from automation. alarms: schedule background housekeeping. offscreen: host the speech-to-text recorder in a hidden document because service workers cannot capture audio directlyLocal service, nativeMessaging: start the optional local agent service described under Local Agent Service and Native Messaging below. Nothing about the pages you visit crosses that channel. system.memory: read the total installed RAM figure only, so FSB can suggest a sensible limit on how many agents run at once. The available-memory and per-processor breakdowns are never read, and the figure never leaves the extensionMicrophone access for speech-to-text is not declared in the manifest. Chrome shows its own permission prompt the first time you use the mic button.Data StorageAll settings and data are stored locally in Chrome's extension storage, with sensitive values encrypted at rest as described in the sections below.Configuration is stored in chrome.storage.localAPI keys, credentials, and payment methods are encrypted before storage, each detailed in its own section belowSession logs are stored locally and can be cleared at any timeAnalytics data (task counts, success rates) stays on your deviceExternal ServicesFSB communicates with external AI providers only when you configure and use a hosted provider. If you use LM Studio, AI requests stay on your machine through its local OpenAI-compatible server. The choice of provider and what data is sent is under your control.Hosted API calls are made only to the provider you select (xAI, OpenAI, Anthropic, Google, or OpenRouter)LM Studio uses a local OpenAI-compatible server on your device and does not require an API keySent data includes: task description, DOM structure summary, and action contextIf you pair Remote Dashboard or use legacy Background Agents sync, an optional relay server handles WebSocket messages for that session. Live-preview frames may pass through the relay transiently, but none of the following is ever persisted on an FSB server: page content, DOM data, browsing history, screenshots, AI prompts, AI responses, cookies, tokens, or site API payloads. This is opt-in only, and the rest of this policy refers back to this list rather than restating itEach provider has their own privacy policy governing how they handle API requestsLocal Agent Service and Native MessagingFSB can be driven by an MCP client running on your own machine. That requires a small local agent service which you install yourself, and the nativeMessaging permission exists solely so the extension can start it. Chrome cannot launch a local program directly, so this is the only supported mechanism.The messaging host io.github.fullselfbrowsing.fsb_native_host is registered only by an explicit action you take, by running fsb-mcp-server install --native-host. The extension cannot install it, and its registration names one permitted extension originThe exchange is a closed schema. FSB sends a version number, an action of either wake or bootstrap, and a locally generated correlation identifier; it receives that identifier back with an outcome and a reason drawn from a fixed list. Any additional field is rejected outright and messages are capped at 4 KB. No URL, tab, page content, or DOM data exists anywhere in this schemaA successful start returns a pairing code. This is a loopback authentication secret generated on your device and stored with owner-only permissions, rotated every time the service starts. It carries no information about you or your browsingThe service listens on your machine only. The extension bridge refuses to bind any address that is not loopback, and the MCP endpoint binds 127.0.0.1 by default and whenever FSB starts itUnder its own directory the service keeps only operational records: a diagnostics log of delegated-run outcomes restricted to a fixed field list, and a process-supervision journal. Task text, environment values, binary paths, and error text never reach either of them, and entries carrying credential-shaped values are discarded rather than writtenIf the host is not installed, the feature simply does not start. Nothing is transmitted, and the rest of the extension continues to work normallyDelegated CLI AgentsFSB can hand a task to a coding-agent command-line tool you have already installed, currently Claude Code or Grok Build. FSB starts that program on your own machine; your task is never proxied through FSB servers.Your task text reaches the tool over its standard input and nowhere else. FSB checks this before every run: if the task appears in the command line, the working directory, the environment, or any file path, the run is aborted rather than started. Tasks are capped at 64 KBWhile the run is in progress the tool calls back into FSB over the loopback connection to drive your browser. Whatever it reads that way — page text, DOM snapshots, spreadsheet values, screenshots — is returned to that tool, and therefore reaches its vendor: Anthropic for Claude Code, xAI for Grok BuildThe run uses the account already signed in to that tool. FSB does not hold, read, or transmit your Anthropic or xAI credential, and any usage or charges follow that vendor's own planGrok Build runs inside a profile directory FSB keeps separate from your own. The sign-in credential stored there is written by the Grok tool itself during its own sign-in flowThe agent instructions FSB supplies state that passwords, CVV values, payment-card data, and saved credentials must never enter prompts, narration, logs, or tool argumentsReading and Filling Google SheetsFSB can read and fill a range in Google Sheets by driving the spreadsheet interface in your own already-authenticated browser session.Reading a range returns those cell values to the model you selected, exactly like any other page content FSB reads on your behalf. Only the range you asked for is read; there is no whole-spreadsheet exportSpreadsheet content is stripped out before anything is written to FSB's local session history. Only shape counts survive — how many rows, columns, and values were involved. The spreadsheet address, sheet name, cell references, and every cell value are discardedIf that filtering cannot be applied to a record for any reason, the record is dropped rather than storedRemote Dashboard and PhantomStream Live PreviewWhen you pair Remote Dashboard, FSB can show a live preview of the active browser tab using PhantomStream. The preview streams structured DOM data instead of pixels: an initial snapshot, MutationObserver diffs, scroll position, automation overlays, dialog state, media playback state, and remote-control messages over WebSocket.The relay forwards those live-preview frames only to the paired dashboard session, and nothing from the preview stream is persisted on FSB servers — see the list under External Services aboveFSB enables PhantomStream input masking (maskInputs: true), so passwords and form-control values are masked before they leave the captured pageThe dashboard viewer renders mirrored content in a scriptless sandbox and sanitizes mirrored DOM and CSS before displayImages, video, and audio are mirrored by reference in current dashboard mode. Media bytes do not cross the relay; the viewer may fetch allowed public HTTPS asset or media URLs directly, while private, internal, non-HTTPS, or otherwise blocked origins are replaced with placeholders by PhantomStream's fail-closed fetch policyNo Third-Party TrackingFSB does not include any third-party analytics, ad trackers, or cross-site fingerprinting. There are no cookies and no third-party scripts beyond the AI provider APIs you explicitly configure. The one piece of first-party data FSB sends home is the opt-out Anonymous Usage Telemetry described below, used solely to power the public /stats dashboard.API KeysYour API keys are encrypted locally using AES-GCM before being stored. They are never transmitted anywhere except to the AI provider you configured, and only as authentication headers in API requests.Keys are encrypted at rest in Chrome storageDecryption only happens in-memory when making API callsKeys are never logged, exported, or sharedAuto-PasswordsFSB includes an optional credential manager that stores login credentials encrypted on your device. Passwords are never exposed to AI models. They are filled directly into pages by the content script, bypassing the AI entirely.Credentials are encrypted at rest using AES-GCM with 256-bit keys and PBKDF2 key derivationWhen the AI analyzes a page, password field values are replaced with [hidden]. The actual password is never included in any AI promptAuto-fill is performed by the content script injecting values directly into the DOM, with no AI involvement in the credential flowThe credential list view only shows usernames and domains. Passwords are decrypted individually and only when needed for auto-fillCredentials are stored per-domain with parent domain fallback (e.g., accounts.google.com inherits from google.com)Payment MethodsFSB includes an optional payment-method vault that stores card details on your device for checkout auto-fill. Cards are treated with the same encryption and AI isolation as login credentials, and the full card number is never sent to any AI model.Card details (number, expiry, cardholder, and zip) are encrypted at rest using AES-GCM with the same vault-derived key used for credentialsWhen the AI analyzes a checkout page, any detected card-number field values are replaced with [hidden] before the prompt is built. Card numbers, CVV, and expiry are never included in any AI promptAuto-fill happens via the content script writing directly into the page's DOM fields, bypassing the AI entirelyThe list view shows only a card nickname and last-4 digits. Full numbers are decrypted in memory only at the moment of fillAn MCP client can request a payment fill via use_payment_method, but the user is shown an in-extension confirmation prompt before any card data is written into the pageCVV is never persisted unless you opt in per-card, and even then it is encrypted alongside the rest of the recordSpeech-to-TextFSB includes an optional microphone input for the prompt box. The default provider runs entirely in your browser; an optional OpenAI Whisper fallback can be enabled in settings if you want higher accuracy.Default provider: the browser's native SpeechRecognition API. Audio is processed by Chrome and never leaves your device through FSBOptional Whisper provider: when sttProvider is set to whisper and an OpenAI key is configured, recorded audio chunks are uploaded directly from your browser to OpenAI's transcription endpoint. FSB never sees or stores the audioThe microphone is only active while you are holding or have toggled the mic button. Chrome prompts for permission the first time you use it; FSB does not request microphone access in the extension manifestTranscripts are inserted into the prompt textarea only and are never logged, persisted, or transmitted outside the active AI request you choose to sendDisable speech entirely by leaving the mic button untouched, or by clearing the optional Whisper provider in Chrome extension storagePrompt Injection PreventionWeb pages can contain hidden text designed to hijack AI agents. FSB implements multi-layered defenses to ensure the AI only follows your instructions, never instructions embedded in page content.All page content is wrapped in [PAGE_CONTENT] boundary markers, and the AI is instructed to never follow instructions found within these markersA sanitization engine strips known injection patterns (e.g., "ignore previous instructions", fake system prompts, override attempts) from all page content before it reaches the AIAI-generated actions are validated before execution. Dangerous URLs (javascript:, data:) and script injection attempts are blockedOnly a strict, fixed allowlist of known tools can be executed. The AI cannot invent or call arbitrary actionsContent size is capped (500 chars per value, 15K total prompt cap) to limit payload deliveryInvisible Unicode control characters that websites embed are stripped before processingBackground Agents and Server SyncDeprecated in v0.9.45rc1. FSB's built-in Background Agents have been superseded by OpenClaw and Claude Routines, with remote control now handled by the Sync tab. The disclosures below are retained for users still running v0.9.44 or earlier; on current builds the relay server is only contacted when you pair a Sync session.If you opt into legacy Background Agents server sync or pair Remote Dashboard, a relay server facilitates communication between your extension and the dashboard.The server stores: agent definitions (name, schedule, target URL), run metrics (token count, cost, duration, success/fail status), and session pairing tokensBeyond that run metadata the server persists nothing derived from the pages you visit — see the list under External Services aboveAuthentication uses hash keys (generated locally) and session tokens that expire after 24 hoursOne-time pairing tokens expire after 60 seconds and cannot be reusedServer sync is disabled by default. You must explicitly enable it in OptionsMemory SystemFSB's memory system stores navigation patterns and site intelligence to improve automation over time.All memory data (semantic, episodic, procedural) is stored locally in chrome.storage.localNo memory data is sent to any external serverMemory can be viewed and cleared at any time from the Options dashboardSite maps and navigation patterns are domain-specific and isolated from each otherSession Replay and ScreenshotsWhen an MCP client drives FSB, the extension records what the agent did so you can review or replay the run afterwards. These records stay on your device.Runs still in progress are held in Chrome's session storage so they survive the service worker being evicted; finished runs are written to chrome.storage.local through the same history store your own automation runs useRecords are redacted before they are stored. Values under credential-shaped keys, text typed into fields that look like password, one-time-code, or card inputs, and signed or token-bearing URL parameters are all replaced. Ordinary addresses and selectors are preserved, because replay does not work without themRecorded runs are kept for 30 days by default, adjustable between 1 and 365 days, and older ones are pruned by a daily job. Recording can be switched off entirely in Advanced SettingsScreenshot images are never written into that history. Only capture metadata such as dimensions and byte size is retainedScreenshots an MCP client requests are saved by the local service as files on your own disk with owner-only permissions and deleted automatically after seven days. The image is also handed back to the requesting client, and therefore to that client's modelAnonymous Usage TelemetryFSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.What we collectA random per-install UUID stored in chrome.storage.local under the key fsbInstallUuid. The UUID is generated locally and never tied to your identity.The name of the MCP client used (e.g. Claude Code, Cursor, Codex), drawn from a fixed allowlist.The model name used for a session (e.g. grok-4-fast, claude-opus-4), drawn from a fixed allowlist.Aggregate input/output token counts per session.The number of active FSB agents on your install (an integer count).What we do NOT collectPage URLs, hostnames, or browsing history.Prompts, instructions, task descriptions, or any natural-language text you send to your model provider.Page DOM, screenshots, page content, site API payloads, or AI responses.Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.Names, usernames, account handles, or any free-form identity fields.Email addresses, phone numbers, or contact information.Region (state-level) metricThe server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs.The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).RetentionRaw events are retained for 7 days. Daily rollups (one row per install per day, including the coarse region label) are retained for 365 days. Global aggregates (one row per day, no per-install dimension) are retained indefinitely so historical charts on /stats remain stable.How to opt outOpen the FSB Control Panel, scroll to Advanced Settings, and toggle Send anonymous usage data off. The change takes effect immediately; no further events will be sent from your install.How to erase your dataTo request erasure of all telemetry rows associated with your install (GDPR Article 17), look up your fsbInstallUuid in Chrome DevTools → Application → Storage → Extension storage, then send a single HTTP request:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Consent and Audit ControlsFSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. + Copia archivada de la política de privacidad tal como estaba desde el 28 de septiembre de 2026, antes de la actualización del 29 de septiembre de 2026.Resumen: FSB se ejecuta dentro de tu navegador y los servidores de FSB no recopilan datos de navegación. Las llamadas de IA van directamente desde tu navegador al proveedor que elijas. Las claves de API y las credenciales se cifran localmente, y los datos de memoria permanecen en tu dispositivo. Si instalas el servicio de agente local opcional, FSB también puede ser controlado por un cliente MCP que se ejecute en tu propia máquina mediante una conexión de bucle invertido, y por las CLIs de agentes de programación que ya tengas instaladas. Los únicos datos que salen de tu máquina hacia FSB son la telemetría de uso anónima con opción de exclusión que alimenta la página pública /stats. Si vinculas Remote Dashboard, los fotogramas transitorios de vista previa en vivo pueden pasar por el relé durante la sesión, pero no se almacenan. Todo es de código abierto y auditable.Recopilación de datosFSB funciona íntegramente dentro de tu navegador. Cuando inicias una tarea de automatización, la extensión puede inspeccionar el DOM (modelo de objetos del documento) de la pestaña activa y, cuando invocas una capacidad, realizar solicitudes a la API del sitio y del mismo origen desde la sesión de tu navegador.No se recopila ni almacena historial de navegación más allá de la sesión actualLos datos del DOM y los resultados de la API del sitio se analizan localmente y se descartan después de cada paso de automatización, salvo que los guardes expresamente en la memoria.No se extrae información personal de las páginas que visitasCapacidades de la API del sitioLa capa de capacidades de FSB puede llamar a la API propia de un sitio desde el contexto de la página mediante tu sesión de navegador ya autenticada. Estas solicitudes se ejecutan localmente en Chrome; el navegador puede adjuntar cookies propias o la autenticación del sitio de destino, pero FSB no devuelve, almacena, registra ni envía cookies, tokens, valores CSRF, cuerpos de solicitudes o cuerpos de respuestas a los servidores de FSB.Las invocaciones de capacidades siguen los controles de consentimiento de FSB y solo se registran en el registro de auditoría local censurado: origen, slug de capacidad, método, clase de efecto secundario, decisión de consentimiento y resultado. El registro de auditoría nunca almacena argumentos de invocación, cuerpos de solicitudes o respuestas, cookies, tokens, valores CSRF ni cargas de respuestas del sitio.Permisos de Chrome que solicita FSBPara ejecutar la automatización web, FSB declara los siguientes permisos en su manifiesto de Chrome. Cada uno se utiliza únicamente para el propósito documentado; nada se envía fuera del dispositivo basándose en ninguno de ellos.DOM y pestañas, activeTab, scripting, tabs, windows, sidePanel y el permiso de host <all_urls>: leer y escribir en la pestaña activa, inyectar el script de contenido de automatización, enumerar y cambiar de pestaña y renderizar el panel lateral.Automatización avanzada, debugger: conectar Chrome DevTools Protocol para realizar clics por coordenadas y acciones de arrastre y pulsación prolongada de teclas que la DOM normal del API no puede ejecutar. webNavigation: observar los eventos de inicio y finalización de la navegación para que la automatización espere el momento adecuado.Almacenamiento local, storage, unlimitedStorage: guardar tu configuración, credenciales, métodos de pago y memoria en chrome.storage.local en tu dispositivo. El almacenamiento ilimitado elimina la cuota predeterminada de 10 MB para que la memoria y los registros de sesión puedan crecer sin alcanzar un límite.Ayudas de UX, clipboardWrite: escribir en el portapapeles los resultados de la automatización. alarms: programar tareas de mantenimiento en segundo plano. offscreen: alojar el grabador de voz a texto en un documento oculto porque los service workers no pueden capturar audio directamente.Servicio local, nativeMessaging: inicia el servicio de agente local opcional descrito en Servicio de agente local y mensajería nativa más abajo. Nada sobre las páginas que visitas pasa por ese canal. system.memory: lee únicamente la cifra de RAM total instalada, para que FSB pueda sugerir un límite razonable de agentes simultáneos. La memoria disponible y el desglose por procesador nunca se leen, y la cifra nunca sale de la extensiónEl acceso al micrófono para voz a texto no está declarado en el manifiesto. Chrome muestra su propia solicitud de permiso la primera vez que utilizas el botón del micrófono.Almacenamiento de datosToda la configuración y los datos se almacenan localmente en el almacenamiento de extensiones de Chrome, con los valores sensibles cifrados en reposo tal como se describe en las secciones siguientes.La configuración se almacena en chrome.storage.localLas claves de API, las credenciales y los métodos de pago se cifran antes de almacenarse; cada uno se detalla en su propia sección más abajoLos registros de sesión se almacenan localmente y se pueden borrar en cualquier momentoLos datos de analítica (conteos de tareas, tasas de éxito) permanecen en tu dispositivoServicios externosFSB se comunica con proveedores de IA externos solo cuando configuras y usas un proveedor alojado. Si usas LM Studio, las solicitudes de IA permanecen en tu máquina a través de su servidor local compatible con OpenAI. La elección de proveedor y qué datos se envían está bajo tu control.Las llamadas a APIs alojadas se hacen solo al proveedor que selecciones (xAI, OpenAI, Anthropic, Google u OpenRouter)LM Studio usa un servidor local compatible con OpenAI en tu dispositivo y no requiere clave de APILos datos enviados incluyen: descripción de la tarea, resumen de la estructura del DOM y contexto de la acción.Si vinculas Remote Dashboard o usas la sincronización heredada de Background Agents, un servidor de relé opcional gestiona los mensajes WebSocket de esa sesión. Los fotogramas de vista previa en vivo pueden pasar transitoriamente por el relé, pero nada de lo siguiente se conserva jamás en un servidor de FSB: contenido de la página, datos DOM, historial de navegación, capturas de pantalla, prompts de IA, respuestas de IA, cookies, tokens o cargas útiles de la API del sitio. Es solo opcional, y el resto de esta política remite a esta lista en lugar de repetirlaCada proveedor tiene su propia política de privacidad que rige cómo maneja las solicitudes de APIServicio de agente local y mensajería nativaFSB puede ser controlado por un cliente MCP que se ejecute en tu propia máquina. Eso requiere un pequeño servicio de agente local que instalas tú mismo, y el permiso nativeMessaging existe únicamente para que la extensión pueda iniciarlo. Chrome no puede lanzar un programa local directamente, así que este es el único mecanismo admitido.El host de mensajería io.github.fullselfbrowsing.fsb_native_host solo se registra mediante una acción explícita tuya, ejecutando fsb-mcp-server install --native-host. La extensión no puede instalarlo, y su registro nombra un único origen de extensión permitidoEl intercambio sigue un esquema cerrado. FSB envía un número de versión, una acción que es o bien wake o bien bootstrap, y un identificador de correlación generado localmente; recibe de vuelta ese identificador con un resultado y un motivo tomados de una lista fija. Cualquier campo adicional se rechaza de plano y los mensajes están limitados a 4 KB. En este esquema no existe ninguna URL, pestaña, contenido de página ni dato DOMUn inicio correcto devuelve un código de emparejamiento. Es un secreto de autenticación de bucle invertido generado en tu dispositivo y almacenado con permisos exclusivos del propietario, que se renueva cada vez que arranca el servicio. No contiene información alguna sobre ti ni sobre tu navegaciónEl servicio escucha únicamente en tu máquina. El puente de la extensión se niega a enlazar a cualquier dirección que no sea de bucle invertido, y el punto final MCP enlaza a 127.0.0.1 de forma predeterminada y siempre que lo inicie FSBEn su propio directorio, el servicio solo conserva registros operativos: un registro de diagnóstico de los resultados de las ejecuciones delegadas, limitado a una lista fija de campos, y un diario de supervisión de procesos. El texto de la tarea, los valores de entorno, las rutas de binarios y los textos de error nunca llegan a ninguno de los dos, y las entradas con valores que parecen credenciales se descartan en lugar de escribirseSi el host no está instalado, la función simplemente no se inicia. No se transmite nada y el resto de la extensión sigue funcionando con normalidadAgentes CLI delegadosFSB puede entregar una tarea a una herramienta de línea de comandos de agente de programación que ya tengas instalada, actualmente Claude Code o Grok Build. FSB inicia ese programa en tu propia máquina; tu tarea nunca pasa por servidores de FSB.El texto de tu tarea llega a la herramienta por su entrada estándar y por ninguna otra vía. FSB lo comprueba antes de cada ejecución: si la tarea aparece en la línea de comandos, el directorio de trabajo, el entorno o cualquier ruta de archivo, la ejecución se aborta en lugar de iniciarse. Las tareas están limitadas a 64 KBMientras la ejecución está en curso, la herramienta vuelve a llamar a FSB por la conexión de bucle invertido para controlar tu navegador. Todo lo que lea de ese modo (texto de la página, instantáneas del DOM, valores de hojas de cálculo, capturas de pantalla) se devuelve a esa herramienta y, por tanto, llega a su proveedor: Anthropic para Claude Code, xAI para Grok BuildLa ejecución usa la cuenta con la que ya has iniciado sesión en esa herramienta. FSB no guarda, lee ni transmite tu credencial de Anthropic ni de xAI, y cualquier uso o cargo se rige por el plan de ese proveedorGrok Build se ejecuta dentro de un directorio de perfil que FSB mantiene separado del tuyo. La credencial de inicio de sesión guardada allí la escribe la propia herramienta Grok durante su propio flujo de accesoLas instrucciones de agente que suministra FSB establecen que las contraseñas, los valores CVV, los datos de tarjetas de pago y las credenciales guardadas nunca deben entrar en prompts, narraciones, registros ni argumentos de herramientasLectura y relleno de Google SheetsFSB puede leer y rellenar un rango en Google Sheets manejando la interfaz de la hoja de cálculo en tu propia sesión de navegador ya autenticada.Al leer un rango, esos valores de celda se envían al modelo que hayas seleccionado, igual que cualquier otro contenido de página que FSB lea en tu nombre. Solo se lee el rango que has pedido; no existe una exportación de la hoja completaEl contenido de la hoja de cálculo se elimina antes de escribir nada en el historial de sesión local de FSB. Solo sobreviven los recuentos de forma: cuántas filas, columnas y valores intervinieron. La dirección de la hoja, el nombre de la pestaña, las referencias de celda y todos los valores se descartanSi ese filtrado no puede aplicarse a un registro por cualquier motivo, el registro se descarta en lugar de almacenarseVista previa en directo del Remote Dashboard y PhantomStreamCuando emparejas el Remote Dashboard, FSB puede mostrar una vista previa en directo de la pestaña activa del navegador mediante PhantomStream. La vista previa transmite datos estructurados del DOM en lugar de píxeles: una instantánea inicial, cambios de MutationObserver, posición de desplazamiento, superposiciones de automatización, estado de los cuadros de diálogo, estado de reproducción multimedia y mensajes de control remoto mediante WebSocket.El relé reenvía esos fotogramas de vista previa en vivo solo a la sesión del panel vinculada, y nada del flujo de vista previa se conserva en los servidores de FSB: consulta la lista en Servicios externos más arribaFSB activa el enmascaramiento de entradas de PhantomStream (maskInputs: true), por lo que las contraseñas y los valores de los controles de formulario se enmascaran antes de salir de la página capturada.El visor del panel renderiza el contenido replicado en un entorno aislado sin scripts y sanea el DOM y el CSS replicados antes de mostrarlos.En el modo actual del panel, las imágenes, el vídeo y el audio se replican por referencia. Los bytes multimedia no atraviesan el relé; el visor puede obtener directamente las URL públicas permitidas de recursos o contenido multimedia HTTPS, mientras que los orígenes privados, internos, no HTTPS o bloqueados por cualquier otro motivo se sustituyen por marcadores de posición conforme a la política de obtención con bloqueo ante fallos de PhantomStream.Sin rastreo de tercerosFSB no incluye ninguna analítica de terceros, rastreadores publicitarios ni fingerprinting entre sitios. No hay cookies ni scripts de terceros más allá de las AI de proveedores de APIs que configuras explícitamente. El único dato de primera parte que FSB envía es la Telemetría de Uso Anónima opt-out descrita más adelante, utilizada únicamente para alimentar el panel público /stats.Claves de APITus claves de API se cifran localmente usando AES-GCM antes de almacenarse. Nunca se transmiten a ningún lugar excepto al proveedor de IA que configuraste, y solo como encabezados de autenticación en las solicitudes de API.Las claves están cifradas en reposo en el almacenamiento de ChromeEl descifrado solo ocurre en memoria al hacer llamadas a la APILas claves nunca se registran, exportan ni compartenContraseñas automáticasFSB incluye un gestor de credenciales opcional que almacena las credenciales de inicio de sesión cifradas en tu dispositivo. Las contraseñas nunca se exponen a los modelos de IA. Se completan directamente en las páginas mediante el content script, evitando por completo a la IA.Las credenciales se cifran en reposo usando AES-GCM con claves de 256 bits y derivación de claves PBKDF2Cuando la IA analiza una página, los valores de los campos de contraseña se reemplazan con [hidden]. La contraseña real nunca se incluye en ningún prompt de IAEl autocompletado lo realiza el script de contenido, que inyecta los valores directamente en el DOM, sin intervención de la IA en el flujo de credenciales.La vista de lista de credenciales solo muestra usuarios y dominios. Las contraseñas se descifran individualmente y solo cuando se necesitan para el autorrellenadoLas credenciales se almacenan por dominio con respaldo al dominio padre (por ejemplo, accounts.google.com hereda de google.com)Métodos de pagoFSB incluye una bóveda opcional de métodos de pago que almacena los datos de la tarjeta en tu dispositivo para autocompletar el pago. Las tarjetas se tratan con el mismo cifrado y aislamiento de IA que las credenciales de inicio de sesión, y el número completo de la tarjeta nunca se envía a ningún modelo de IA.Los datos de la tarjeta (número, caducidad, titular y código postal) se cifran en reposo con AES-GCM usando la misma clave derivada de la bóveda que se utiliza para las credencialesCuando la IA analiza una página de pago, los valores detectados en los campos del número de tarjeta se sustituyen por [hidden] antes de construir el prompt. Los números de tarjeta, el CVV y la caducidad nunca se incluyen en ningún prompt de IAEl autocompletado se realiza mediante el script de contenido escribiendo directamente en los campos DOM de la página, evitando por completo a la IALa vista de lista solo muestra un alias de tarjeta y los últimos 4 dígitos. Los números completos solo se descifran en memoria y únicamente en el momento del autocompletadoUn cliente MCP puede solicitar un autocompletado de pago mediante use_payment_method, pero al usuario se le muestra una confirmación dentro de la extensión antes de escribir cualquier dato de la tarjeta en la páginaEl CVV nunca se persiste salvo que actives esa opción por cada tarjeta; e incluso así se cifra junto con el resto del registroVoz a textoFSB incluye una entrada de micrófono opcional para el cuadro del prompt. El proveedor por defecto se ejecuta completamente en tu navegador; en los ajustes puedes habilitar opcionalmente un fallback de OpenAI Whisper si quieres mayor precisión.Proveedor predeterminado: la SpeechRecognition API nativa del navegador. Chrome procesa el audio, que nunca sale de tu dispositivo a través de FSB.Proveedor Whisper opcional: cuando sttProvider está configurado como whisper y se ha configurado una clave de OpenAI, los fragmentos de audio grabados se suben directamente desde tu navegador al endpoint de transcripción de OpenAI. FSB nunca ve ni almacena el audioEl micrófono solo está activo mientras mantienes pulsado o has alternado el botón del micrófono. Chrome solicita el permiso la primera vez que lo utilizas; FSB no solicita el acceso al micrófono en el manifiesto de la extensiónLas transcripciones se insertan únicamente en el área de texto del prompt y nunca se registran, persisten ni transmiten fuera de la solicitud activa de IA que decides enviarPara deshabilitar la voz por completo, deja sin tocar el botón del micrófono, o borra el proveedor opcional de Whisper en el almacenamiento de la extensión de ChromePrevención de inyección de promptsLas páginas web pueden contener texto oculto diseñado para secuestrar agentes de IA. FSB implementa defensas en múltiples capas para asegurar que la IA solo siga tus instrucciones, nunca instrucciones incrustadas en el contenido de la página.Todo el contenido de la página se envuelve en marcadores de límite [PAGE_CONTENT], y se instruye a la IA a nunca seguir instrucciones encontradas dentro de estos marcadoresUn motor de saneamiento elimina patrones de inyección conocidos (por ejemplo, "ignora las instrucciones previas", prompts de sistema falsos, intentos de anulación) de todo el contenido de la página antes de que llegue a la IALas acciones generadas por la IA se validan antes de ejecutarse. URLs peligrosas (javascript:, data:) e intentos de inyección de scripts se bloqueanSolo se puede ejecutar una lista fija y estricta de herramientas conocidas. La IA no puede inventar ni llamar a acciones arbitrarias.El tamaño del contenido está limitado (500 caracteres por valor, tope total de prompt de 15K) para restringir la entrega de carga útilLos caracteres de control Unicode invisibles que incrustan los sitios web se eliminan antes del procesamientoAgentes en segundo plano y sincronización con servidorObsoleto desde v0.9.45rc1. Los Background Agents integrados de FSB han sido sustituidos por OpenClaw y Claude Routines, mientras que el control remoto ahora se gestiona desde la pestaña Sync. La información siguiente se conserva para quienes aún utilizan v0.9.44 o una versión anterior; en las versiones actuales solo se contacta con el servidor de relé cuando emparejas una sesión Sync.Si activas la sincronización heredada de Background Agents con el servidor o emparejas el Remote Dashboard, un servidor de relé facilita la comunicación entre la extensión y el panel.El servidor almacena: definiciones de agentes (nombre, programación, URL objetivo), métricas de ejecución (conteo de tokens, costo, duración, estado de éxito/fallo) y tokens de emparejamiento de sesiónMás allá de esos metadatos de ejecución, el servidor no conserva nada derivado de las páginas que visitas: consulta la lista en Servicios externos más arribaLa autenticación usa claves hash (generadas localmente) y tokens de sesión que expiran después de 24 horasLos tokens de emparejamiento de un solo uso expiran después de 60 segundos y no pueden reutilizarseLa sincronización con servidor está deshabilitada por defecto. Debes habilitarla explícitamente en OpcionesSistema de memoriaEl sistema de memoria de FSB almacena patrones de navegación e inteligencia de sitios para mejorar la automatización con el tiempo.Todos los datos de memoria (semántica, episódica, procedimental) se almacenan localmente en chrome.storage.localNingún dato de memoria se envía a ningún servidor externoLa memoria se puede ver y borrar en cualquier momento desde el panel de opcionesLos mapas de sitios y patrones de navegación son específicos del dominio y están aislados entre síReproducción de sesiones y capturas de pantallaCuando un cliente MCP controla FSB, la extensión registra lo que hizo el agente para que puedas revisar o reproducir la ejecución más tarde. Esos registros permanecen en tu dispositivo.Las ejecuciones aún en curso se guardan en el almacenamiento de sesión de Chrome para que sobrevivan al desalojo del service worker; las ejecuciones terminadas se escriben en chrome.storage.local, el mismo almacén de historial que usan tus propias automatizacionesLos registros se depuran antes de almacenarse. Se sustituyen los valores bajo claves que parecen credenciales, el texto escrito en campos que parecen de contraseña, código de un solo uso o tarjeta, y los parámetros de URL firmados o portadores de tokens. Las direcciones y los selectores corrientes se conservan, porque sin ellos la reproducción no funcionaLas ejecuciones registradas se conservan 30 días de forma predeterminada, ajustable entre 1 y 365 días, y las más antiguas las elimina un trabajo diario. El registro puede desactivarse por completo en los ajustes avanzadosLas imágenes de las capturas de pantalla nunca se escriben en ese historial. Solo se conservan metadatos de la captura, como las dimensiones y el tamaño en bytesLas capturas de pantalla que solicita un cliente MCP las guarda el servicio local como archivos en tu propio disco con permisos exclusivos del propietario y las elimina automáticamente a los siete días. La imagen también se devuelve al cliente solicitante y, por tanto, a su modeloTelemetría anónima de usoFSB v0.9.69 introdujo una canalización de telemetría de uso anónima con opción de exclusión para que el proyecto pueda publicar cifras de adopción agregadas (consulta /stats) sin tocar nunca las páginas que navegas. La única señal de ubicación es una etiqueta aproximada de país/estado que el servidor deriva de la IP de tu solicitud en el momento de la ingesta (nunca del contenido de la página), publicada solo de forma agregada: consulta Métrica de región (a nivel de estado) más abajo. La telemetría está activada de forma predeterminada, pero puede desactivarse con un solo interruptor, y los datos por instalación pueden borrarse a petición.Lo que recopilamosUn UUID aleatorio por instalación, almacenado en chrome.storage.local bajo la clave fsbInstallUuid. El UUID se genera localmente y nunca se vincula a tu identidad.El nombre del cliente MCP usado (p. ej. Claude Code, Cursor, Codex), extraído de una lista de permitidos fija.El nombre del modelo utilizado en una sesión (p. ej. grok-4-fast, claude-opus-4), extraído de una lista de permitidos fija.Recuentos agregados de tokens de entrada/salida por sesión.El número de agentes FSB activos en tu instalación (un recuento entero).Lo que NO recopilamosURLs de páginas, nombres de host o historial de navegación.Prompts, instrucciones, descripciones de tareas o cualquier texto en lenguaje natural que envíes a tu proveedor de modelos.El DOM y el contenido de la página, capturas de pantalla, cargas de la API del sitio o respuestas de AI.Direcciones IP en texto plano. El servidor usa la IP de la solicitud de forma transitoria y en línea para exactamente tres fines —un hash con sal rotada a diario para la limitación de tasa, la derivación de una etiqueta aproximada de país/estado (ver más abajo) y la anotación de si la dirección es IPv4 o IPv6— y luego la descarta de inmediato. La IP en texto plano nunca se almacena ni se registra.Nombres, nombres de usuario, identificadores de cuenta o cualquier campo de identidad de forma libre.Direcciones de correo electrónico, números de teléfono o información de contacto.Métrica de región (a nivel de estado)El servidor deriva una etiqueta aproximada de país y estado de EE. UU. (subdivisión) a partir de la IP de tu solicitud en el momento de la ingesta, usando un conjunto de datos DB-IP IP-to-City Lite autoalojado y nuestra propia búsqueda: no un servicio de geolocalización de terceros en vivo, y nunca MaxMind. La IP en texto plano se procesa en línea y se descarta; solo se conservan la etiqueta derivada y la familia de direcciones (IPv4 o IPv6).La región se publica solo de forma agregada, tras un umbral de anonimato k≥5: cualquier estado con menos de 5 instalaciones distintas se agrupa en un único cubo «Other» (suprimido por completo cuando incluso ese recuento combinado es inferior a 5). Ninguna etiqueta de estado publicada representa jamás menos de 5 instalaciones.La etiqueta aproximada se almacena en el evento bruto (eliminado por la retención de 7 días) y en el resumen diario de tu instalación, que se conserva durante 365 días para que /stats pueda contar cada instalación una sola vez según su región más reciente. Nunca es una dirección IP, una ciudad ni unas coordenadas; se elimina junto con el resto de los datos de tu instalación cuando solicitas el borrado; y solo se publica tras el umbral k≥5 descrito arriba. Datos de geolocalización por DB-IP (https://db-ip.com).RetenciónLos eventos en bruto se retienen durante 7 días. Los agregados diarios (una fila por instalación por día, incluida la etiqueta aproximada de región) se retienen durante 365 días. Los agregados globales (una fila por día, sin dimensión por instalación) se retienen indefinidamente para que los gráficos históricos en /stats se mantengan estables.Cómo darse de bajaAbre el Panel de control de FSB, desplázate a Ajustes avanzados y desactiva el interruptor Enviar datos de uso anónimos. El cambio tiene efecto inmediato; no se enviarán más eventos desde tu instalación.Cómo borrar tus datosPara solicitar la eliminación de todas las filas de telemetría asociadas a tu instalación (artículo 17 del GDPR), busca tu fsbInstallUuid en Chrome DevTools → Aplicación → Almacenamiento → Almacenamiento de extensiones y envía una única solicitud HTTP:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetAfirmación de Uso LimitadoLa telemetría anónima de uso de FSB se usa únicamente para calcular estadísticas de uso agregadas mostradas públicamente en full-selfbrowsing.com/stats. Los datos nunca se venden, nunca se comparten con terceros, nunca se usan para publicidad y nunca se usan para entrenar modelos de aprendizaje automático. Este compromiso satisface el requisito de Chrome Web Store de Limited Use.Métricas públicas agregadasPublicamos métricas agregadas derivadas de este pipeline de telemetría en /stats. Solo se muestran conteos y totales; nunca se expone ninguna fila por instalación.Controles de consentimiento y auditoríaLa postura de automatización de FSB permite la exclusión: los orígenes que no están en la lista de denegación heredan un valor global predeterminado configurable por el usuario, que actualmente se entrega como Auto, mientras que las políticas explícitas por origen pueden configurarlo como Off, Ask o Auto. La retención del registro de auditoría es local y limitada, con controles para exportarlo y borrarlo disponibles en el Panel de control.La lista de denegación de servicios sigue siendo un bloqueo estricto. Los orígenes denegados no se pueden habilitar, independientemente del valor global predeterminado o de cualquier política por origen almacenada.Los orígenes sensibles pueden realizar lecturas con Auto, pero las escrituras vuelven a exigir la autorización por origen para mutaciones antes de ejecutarse. Los orígenes no sensibles pueden excluirse o cambiarse a Ask desde la sección Consentimiento y auditoría del Panel de control.Cada llamada a una capacidad se registra en un registro de auditoría local redactado y de solo anexión. Nunca se almacenan argumentos, tokens, cookies ni cuerpos de respuesta.Código abiertoFSB es totalmente de código abierto bajo la licencia MIT. Puedes auditar cada línea de código para verificar estas afirmaciones sobre privacidad. El código fuente está disponible en GitHub.Cambios en esta políticaSi se actualiza esta política, los cambios se reflejarán por la fecha de "Última actualización" en la parte superior de esta página. Los cambios significativos también se anotarán en las notas de versión del proyecto en GitHub.ContactoSi tienes preguntas sobre esta política de privacidad o el tratamiento de datos de FSB, abre una incidencia en GitHub Issues. + + src/app/pages/privacy/privacy-history-archive.component.html + 7,238 + + August 2026v0.9.91 — Local Agent Service and Native Messaging, Delegated CLI Agents, Google Sheets, Session Replay and Screenshots (full archived text) Agosto de 2026v0.9.91 — Servicio de agente local y mensajería nativa, Agentes CLI delegados, Google Sheets, Reproducción de sesiones y capturas de pantalla (texto archivado completo) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 242,245 @@ -9559,7 +9579,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetAfirmación de Uso LimitadoLa telemetría anónima de uso de FSB se usa únicamente para calcular estadísticas de uso agregadas mostradas públicamente en full-selfbrowsing.com/stats. Los datos nunca se venden, nunca se comparten con terceros, nunca se usan para publicidad y nunca se usan para entrenar modelos de aprendizaje automático. Este compromiso satisface el requisito de Chrome Web Store de Limited Use.Métricas públicas agregadasPublicamos métricas agregadas derivadas de este pipeline de telemetría en /stats. Solo se muestran conteos y totales; nunca se expone ninguna fila por instalación.Controles de consentimiento y auditoríaLa postura de automatización de FSB permite la exclusión: los orígenes que no están en la lista de denegación heredan un valor global predeterminado configurable por el usuario, que actualmente se entrega como Auto, mientras que las políticas explícitas por origen pueden configurarlo como Off, Ask o Auto. La retención del registro de auditoría es local y limitada, con controles para exportarlo y borrarlo disponibles en el Panel de control.La lista de denegación de servicios sigue siendo un bloqueo estricto. Los orígenes denegados no se pueden habilitar, independientemente del valor global predeterminado o de cualquier política por origen almacenada.Los orígenes sensibles pueden realizar lecturas con Auto, pero las escrituras vuelven a exigir la autorización por origen para mutaciones antes de ejecutarse. Los orígenes no sensibles pueden excluirse o cambiarse a Ask desde la sección Consentimiento y auditoría del Panel de control.Cada llamada a una capacidad se registra en un registro de auditoría local redactado y de solo anexión. Nunca se almacenan argumentos, tokens, cookies ni cuerpos de respuesta.Código abiertoFSB es totalmente de código abierto bajo la licencia MIT. Puedes auditar cada línea de código para verificar estas afirmaciones sobre privacidad. El código fuente está disponible en GitHub.Cambios en esta políticaSi se actualiza esta política, los cambios se reflejarán por la fecha de "Última actualización" en la parte superior de esta página. Los cambios significativos también se anotarán en las notas de versión del proyecto en GitHub.ContactoSi tienes preguntas sobre esta política de privacidad o el tratamiento de datos de FSB, abre una incidencia en GitHub Issues. src/app/pages/privacy/privacy-history-archive.component.html - 7,238 + 246,477 @@ -9567,7 +9587,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetMayo – julio de 2026v0.9.90 — Capacidades de la API del sitio, Remote Dashboard y PhantomStream, Métodos de pago, Voz a texto, Métrica de región (texto archivado completo) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 481,484 @@ -9579,7 +9599,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetDeclaración de uso limitadoFSBsolo utiliza la telemetría anónima de uso para calcular estadísticas agregadas que se muestran públicamente en full-selfbrowsing.com/stats. Los datos nunca se venden, comparten con terceros, usan para publicidad ni emplean para entrenar modelos de aprendizaje automático. Este compromiso satisface el requisito de Chrome Web Store Limited Use .Métricas públicas agregadasPublicamos métricas agregadas derivadas de este sistema de telemetría en /stats. Solo se muestran cantidades y totales; nunca se expone una fila por instalación.Controles de consentimiento y auditoríaFSBpermite la exclusión: los orígenes que no están en la lista de denegación heredan un valor global predeterminado configurable por el usuario, que actualmente se entrega como Auto, mientras que las políticas explícitas por origen pueden configurarlo como Off, Ask o Auto. La retención del registro de auditoría es local y limitada, con controles para exportarlo y borrarlo disponibles en el Panel de control.La lista de denegación de servicios sigue siendo un bloqueo estricto. Los orígenes denegados no se pueden habilitar, independientemente del valor global predeterminado o de cualquier política por origen almacenada.Los orígenes sensibles pueden realizar lecturas con Auto, pero las escrituras vuelven a exigir la autorización por origen para mutaciones antes de ejecutarse. Los orígenes no sensibles pueden excluirse o cambiarse a Ask desde la sección Consentimiento y auditoría del Panel de control.Cada llamada a una capacidad se registra en un registro de auditoría local, censurado y de solo anexado. Nunca se almacenan argumentos, tokens, cookies ni cuerpos de respuestas.Código abiertoFSB es totalmente de código abierto bajo la licencia MIT . Puedes auditar cada línea de código para verificar estas afirmaciones sobre privacidad. El código fuente está disponible en GitHubde verdad.Cambios en esta políticaSi se actualiza esta política, los cambios se reflejarán en la fecha "Última actualización" de la parte superior de esta página. Los cambios importantes también se indicarán en las notas de la versión del proyecto en GitHub .ContactoSi tienes preguntas sobre esta política de privacidad o el tratamiento de datos de FSB, abre una incidencia en GitHub Issuesde verdad. src/app/pages/privacy/privacy-history-archive.component.html - 7,198 + 485,676 @@ -9587,7 +9607,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetmarzo de 2026v9.0.2 — Agentes en segundo plano, sistema de memoria, sincronización con el servidor, telemetría anónima de uso y postura legal (texto archivado completo) src/app/pages/privacy/privacy-history-archive.component.html - 202,205 + 680,683 @@ -9599,7 +9619,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetDeclaración de uso limitadoFSB solo utiliza la telemetría anónima de uso para calcular estadísticas agregadas que se muestran públicamente en full-selfbrowsing.com/stats. Los datos nunca se venden, comparten con terceros, usan para publicidad ni emplean para entrenar modelos de aprendizaje automático. Este compromiso satisface el requisito de Chrome Web Store sobre Limited Use.Métricas públicas agregadasPublicamos métricas agregadas derivadas de este sistema de telemetría en /stats. Solo se muestran cantidades y totales; nunca se expone una fila por instalación.Postura legal y modelo de consentimientoFSB presentó su postura de automatización, la retención del registro de auditoría y el modelo de consentimiento por origen como controles explícitos del producto. FSB no hace nada en un origen hasta que lo autorizas expresamente; el acceso automatizado de lectura nunca implica acceso de escritura, y una lista de denegación conservadora bloquea por completo la automatización en categorías sensibles (servicios financieros y gubernamentales).El consentimiento por origen está desactivado de forma predeterminada. El acceso Auto y el acceso de escritura (mutación) son autorizaciones independientes y explícitas que se gestionan desde la sección Consentimiento y auditoría del Panel de control.Cada llamada a una capacidad se registra en un registro de auditoría local, censurado y de solo anexado. Nunca se almacenan argumentos, tokens, cookies ni cuerpos de respuestas.Una lista de denegación de servicios impide habilitar los orígenes sensibles; se aplica en la puerta de capacidades, no solo en la interfaz.Código abiertoFSB es totalmente de código abierto bajo la licencia MIT. Puedes auditar cada línea de código para verificar estas afirmaciones sobre privacidad. El código fuente está disponible en GitHub.Cambios en esta políticaSi se actualiza esta política, los cambios se reflejarán en la fecha "Última actualización" de la parte superior de esta página. Los cambios importantes también se indicarán en las notas de la versión del proyecto en GitHub.ContactoSi tienes preguntas sobre esta política de privacidad o el tratamiento de datos de FSB, abre una incidencia en GitHub Issues. src/app/pages/privacy/privacy-history-archive.component.html - 206,345 + 684,823 @@ -9607,7 +9627,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetfebrero de 2026v0.9 — Política de privacidad inicial (texto archivado completo) src/app/pages/privacy/privacy-history-archive.component.html - 349,352 + 827,830 @@ -9615,7 +9635,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetCopia archivada de la política de privacidad inicial tal como estaba en febrero de 2026. Esta instantánea se reproduce a partir de la página de presentación estática original, sin la interfaz de la página y conservando el contenido de la política.Recopilación de datosFSB funciona íntegramente dentro de tu navegador. La extensión solo accede al DOM (modelo de objetos del documento) de la pestaña activa cuando inicias una tarea de automatización.No se recopila ni almacena el historial de navegación más allá de la sesión actual.DOM se analizan localmente y se descartan después de cada paso de automatización.No se extrae información personal de las páginas que visitas.Almacenamiento de datosToda la configuración y los datos se almacenan localmente en el almacenamiento para extensiones de Chrome. FSB utiliza el cifrado AES-GCM para datos sensibles, como las claves de API.La configuración se almacena en chrome.storage.localLas claves de API se cifran antes de almacenarse mediante AES-GCMLos registros de sesión se almacenan localmente y pueden borrarse en cualquier momento.Los datos analíticos (cantidad de tareas y tasas de éxito) permanecen en tu dispositivo.Servicios externosFSB solo se comunica con proveedores externos de IA cuando los configuras y utilizas. Tú controlas el proveedor elegido y los datos que se envían.Las llamadas a la API solo se realizan al proveedor que seleccionas (xAI, OpenAI, Anthropic o Google).Los datos enviados incluyen: descripción de la tarea, resumen de la estructura del DOM y contexto de la acción.No se envían datos a los servidores de FSB; no existe ninguno.Cada proveedor tiene su propia política de privacidad para el tratamiento de las solicitudes de API.Sin seguimientoFSB no incluye servicios de análisis, telemetría ni seguimiento. No hay cookies, huellas digitales ni scripts de terceros aparte de las API de los proveedores de IA que configuras expresamente.Claves de APITus claves de API se cifran localmente mediante AES-GCM antes de almacenarse. Nunca se transmiten a ningún lugar salvo al proveedor de IA que configuraste, y solo como encabezados de autenticación en las solicitudes de API.Las claves se cifran en reposo en el almacenamiento de ChromeEl descifrado solo se realiza en memoria al efectuar llamadas a la API.Las claves nunca se registran, exportan ni comparten.Contraseñas automáticas (beta)FSB incluye un gestor de credenciales opcional que almacena cifradas las credenciales de inicio de sesión en tu dispositivo. Las contraseñas nunca se exponen a los modelos de IA: el script de contenido las introduce directamente en las páginas, sin intervención alguna de la IA.Las credenciales se cifran en reposo mediante AES-GCM con claves de 256 bits y derivación de claves PBKDF2.Cuando la IA analiza una página, los valores de los campos de contraseña se sustituyen por [hidden]. La contraseña real nunca se incluye en ninguna instrucción enviada a la IA.El autocompletado lo realiza el script de contenido, que inyecta los valores directamente en el DOM, sin intervención de la IA en el flujo de credenciales.La vista de la lista de credenciales solo muestra nombres de usuario y dominios. Las contraseñas se descifran individualmente y únicamente cuando se necesitan para el autocompletado.Las credenciales se almacenan por dominio con alternativa al dominio principal (por ejemplo, accounts.google.com hereda de google.com).Prevención de la inyección de instruccionesLas páginas web pueden contener texto oculto diseñado para secuestrar agentes de IA. FSB aplica defensas de varias capas para garantizar que la IA solo siga tus instrucciones y nunca las que estén incrustadas en el contenido de la página.Todo el contenido de la página se envuelve en marcadores de límite [PAGE_CONTENT], y se indica a la IA que nunca siga las instrucciones que encuentre dentro de esos marcadores.Un motor de saneamiento elimina patrones de inyección conocidos (por ejemplo, "ignora las instrucciones anteriores", instrucciones falsas del sistema e intentos de anulación) de todo el contenido de la página antes de que llegue a la IA.Las acciones generadas por la IA se validan antes de ejecutarse. Se bloquean las URL peligrosas (javascript:, data:) y los intentos de inyectar scripts.Solo se puede ejecutar una lista estricta de más de 30 herramientas conocidas. La IA no puede inventar ni llamar a acciones arbitrarias.El tamaño del contenido está limitado (500 caracteres por valor y un máximo total de 15K en las instrucciones) para restringir la entrega de cargas.Los caracteres de control Unicode invisibles que incrustan los sitios web se eliminan antes del procesamiento.Código abiertoFSB es totalmente de código abierto bajo la licencia MIT. Puedes auditar cada línea de código para verificar estas afirmaciones sobre privacidad. El código fuente está disponible en GitHub.Ver el código fuente en GitHubResumenFSB procesa los datos localmente, cifra la información sensible, nunca expone las contraseñas a modelos de IA, se protege contra ataques de inyección de instrucciones, solo se comunica con los proveedores de IA que eliges, no incluye seguimiento y se puede auditar por completo como software de código abierto. src/app/pages/privacy/privacy-history-archive.component.html - 353,420 + 831,898 @@ -9635,8 +9655,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Last updated: September 2026 - Última actualización: septiembre de 2026 + Last updated: September 29, 2026 + Última actualización: 29 de septiembre de 2026 src/app/pages/privacy/privacy-page.component.html 6,9 @@ -10563,8 +10583,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. - FSB v0.9.69 introdujo una canalización de telemetría de uso anónima con opción de exclusión para que el proyecto pueda publicar cifras de adopción agregadas (consulta /stats) sin tocar nunca las páginas que navegas. La única señal de ubicación es una etiqueta aproximada de país/estado que el servidor deriva de la IP de tu solicitud en el momento de la ingesta (nunca del contenido de la página), publicada solo de forma agregada: consulta Métrica de región (a nivel de estado) más abajo. La telemetría está activada de forma predeterminada, pero puede desactivarse con un solo interruptor, y los datos por instalación pueden borrarse a petición. + FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse place label — city, state or province, and country — that the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (city-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. + FSB v0.9.69 introdujo una canalización de telemetría de uso anónima con opción de exclusión para que el proyecto pueda publicar cifras de adopción agregadas (consulta /stats) sin tocar nunca las páginas que navegas. La única señal de ubicación es una etiqueta aproximada de lugar —ciudad, estado o provincia, y país— que el servidor deriva de la IP de tu solicitud en el momento de la ingesta (nunca del contenido de la página), publicada solo de forma agregada: consulta Métrica de región (a nivel de ciudad) más abajo. La telemetría está activada de forma predeterminada, pero puede desactivarse con un solo interruptor, y los datos por instalación pueden borrarse a petición. src/app/pages/privacy/privacy-page.component.html 197,199 @@ -10651,8 +10671,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. - Direcciones IP en texto plano. El servidor usa la IP de la solicitud de forma transitoria y en línea para exactamente tres fines —un hash con sal rotada a diario para la limitación de tasa, la derivación de una etiqueta aproximada de país/estado (ver más abajo) y la anotación de si la dirección es IPv4 o IPv6— y luego la descarta de inmediato. La IP en texto plano nunca se almacena ni se registra. + Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse city, state, and country label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. + Direcciones IP en texto plano. El servidor usa la IP de la solicitud de forma transitoria y en línea para exactamente tres fines —un hash con sal rotada a diario para la limitación de tasa, la derivación de una etiqueta aproximada de ciudad, estado y país (ver más abajo) y la anotación de si la dirección es IPv4 o IPv6— y luego la descarta de inmediato. La IP en texto plano nunca se almacena ni se registra. src/app/pages/privacy/privacy-page.component.html 213,214 @@ -10675,43 +10695,51 @@ https://full-selfbrowsing.com/api/telemetry/forget - Region (state-level) metric - Métrica de región (a nivel de estado) + Region (city-level) metric + Métrica de región (a nivel de ciudad) src/app/pages/privacy/privacy-page.component.html 218,220 - The server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. - El servidor deriva una etiqueta aproximada de país y estado de EE. UU. (subdivisión) a partir de la IP de tu solicitud en el momento de la ingesta, usando un conjunto de datos DB-IP IP-to-City Lite autoalojado y nuestra propia búsqueda: no un servicio de geolocalización de terceros en vivo, y nunca MaxMind. La IP en texto plano se procesa en línea y se descarta; solo se conservan la etiqueta derivada y la familia de direcciones (IPv4 o IPv6). + The server derives a coarse place label — country, state or province, and city — from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. + El servidor deriva una etiqueta aproximada de lugar —país, estado o provincia, y ciudad— a partir de la IP de tu solicitud en el momento de la ingesta, usando un conjunto de datos DB-IP IP-to-City Lite autoalojado y nuestra propia búsqueda: no un servicio de geolocalización de terceros en vivo, y nunca MaxMind. La IP en texto plano se procesa en línea y se descarta; solo se conservan la etiqueta derivada y la familia de direcciones (IPv4 o IPv6). src/app/pages/privacy/privacy-page.component.html 220,221 - Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs. - La región se publica solo de forma agregada, tras un umbral de anonimato k≥5: cualquier estado con menos de 5 instalaciones distintas se agrupa en un único cubo «Other» (suprimido por completo cuando incluso ese recuento combinado es inferior a 5). Ninguna etiqueta de estado publicada representa jamás menos de 5 instalaciones. + Region is published only in aggregate, behind a k≥5 anonymity floor applied level by level: an install is counted under its city when at least 5 distinct installs share that city, otherwise under its state or province, then its country, by the same rule. Anything still below 5 collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). Each install is counted in exactly one bucket, and no published city, state, or country label ever represents fewer than 5 installs. + La región se publica solo de forma agregada, tras un umbral de anonimato k≥5 aplicado nivel por nivel: una instalación se cuenta en su ciudad cuando al menos 5 instalaciones distintas comparten esa ciudad; si no, en su estado o provincia y, después, en su país, con la misma regla. Lo que siga por debajo de 5 se agrupa en un único cubo «Other» (suprimido por completo cuando incluso ese recuento combinado es inferior a 5). Cada instalación se cuenta en exactamente un cubo, y ninguna etiqueta publicada de ciudad, estado o país representa jamás menos de 5 instalaciones. src/app/pages/privacy/privacy-page.component.html 221 - - The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). - La etiqueta aproximada se almacena en el evento bruto (eliminado por la retención de 7 días) y en el resumen diario de tu instalación, que se conserva durante 365 días para que /stats pueda contar cada instalación una sola vez según su región más reciente. Nunca es una dirección IP, una ciudad ni unas coordenadas; se elimina junto con el resto de los datos de tu instalación cuando solicitas el borrado; y solo se publica tras el umbral k≥5 descrito arriba. Datos de geolocalización por DB-IP (https://db-ip.com). + + The /stats globe places each published label at the approximate center of that city, state, or country, taken from the same dataset — never at the location of any install. + El globo de /stats sitúa cada etiqueta publicada en el centro aproximado de esa ciudad, estado o país, tomado del mismo conjunto de datos, y nunca en la ubicación de ninguna instalación. src/app/pages/privacy/privacy-page.component.html 222,223 + + The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, street address, or coordinates — a city name is the finest detail kept; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). + La etiqueta aproximada se almacena en el evento bruto (eliminado por la retención de 7 días) y en el resumen diario de tu instalación, que se conserva durante 365 días para que /stats pueda contar cada instalación una sola vez según su región más reciente. Nunca es una dirección IP, una dirección postal ni unas coordenadas: el nombre de una ciudad es el detalle más fino que se conserva; se elimina junto con el resto de los datos de tu instalación cuando solicitas el borrado; y solo se publica tras el umbral k≥5 descrito arriba. Datos de geolocalización por DB-IP (https://db-ip.com). + + src/app/pages/privacy/privacy-page.component.html + 223,224 + + Retention Retención src/app/pages/privacy/privacy-page.component.html - 225,226 + 226,227 @@ -10719,7 +10747,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLos eventos en bruto se retienen durante 7 días. Los agregados diarios (una fila por instalación por día, incluida la etiqueta aproximada de región) se retienen durante 365 días. Los agregados globales (una fila por día, sin dimensión por instalación) se retienen indefinidamente para que los gráficos históricos en /stats se mantengan estables. src/app/pages/privacy/privacy-page.component.html - 226,228 + 227,229 @@ -10727,7 +10755,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetCómo darse de baja src/app/pages/privacy/privacy-page.component.html - 228,229 + 229,230 @@ -10735,7 +10763,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetAbre el Panel de control de FSB, desplázate a Ajustes avanzados y desactiva el interruptor Enviar datos de uso anónimos. El cambio tiene efecto inmediato; no se enviarán más eventos desde tu instalación. src/app/pages/privacy/privacy-page.component.html - 229,231 + 230,232 @@ -10743,7 +10771,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetCómo borrar tus datos src/app/pages/privacy/privacy-page.component.html - 231,232 + 232,233 @@ -10751,7 +10779,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetPara solicitar la eliminación de todas las filas de telemetría asociadas a tu instalación (artículo 17 del GDPR), busca tu fsbInstallUuid en Chrome DevTools → Aplicación → Almacenamiento → Almacenamiento de extensiones y envía una única solicitud HTTP: src/app/pages/privacy/privacy-page.component.html - 232,233 + 233,234 @@ -10763,7 +10791,7 @@ https://full-selfbrowsing.com/api/telemetry/forget src/app/pages/privacy/privacy-page.component.html - 233,237 + 234,238 @@ -10771,7 +10799,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetAfirmación de Uso Limitado src/app/pages/privacy/privacy-page.component.html - 237,238 + 238,239 @@ -10779,7 +10807,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLa telemetría anónima de uso de FSB se usa únicamente para calcular estadísticas de uso agregadas mostradas públicamente en full-selfbrowsing.com/stats. Los datos nunca se venden, nunca se comparten con terceros, nunca se usan para publicidad y nunca se usan para entrenar modelos de aprendizaje automático. Este compromiso satisface el requisito de Chrome Web Store de Limited Use. src/app/pages/privacy/privacy-page.component.html - 238,240 + 239,241 @@ -10787,7 +10815,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetMétricas públicas agregadas src/app/pages/privacy/privacy-page.component.html - 240,241 + 241,242 @@ -10795,7 +10823,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetPublicamos métricas agregadas derivadas de este pipeline de telemetría en /stats. Solo se muestran conteos y totales; nunca se expone ninguna fila por instalación. src/app/pages/privacy/privacy-page.component.html - 241,243 + 242,244 @@ -10803,7 +10831,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetControles de consentimiento y auditoría src/app/pages/privacy/privacy-page.component.html - 244,245 + 245,246 @@ -10811,7 +10839,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLa postura de automatización de FSB permite la exclusión: los orígenes que no están en la lista de denegación heredan un valor global predeterminado configurable por el usuario, que actualmente se entrega como Auto, mientras que las políticas explícitas por origen pueden configurarlo como Off, Ask o Auto. La retención del registro de auditoría es local y limitada, con controles para exportarlo y borrarlo disponibles en el Panel de control. src/app/pages/privacy/privacy-page.component.html - 245,247 + 246,248 @@ -10819,7 +10847,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLa lista de denegación de servicios sigue siendo un bloqueo estricto. Los orígenes denegados no se pueden habilitar, independientemente del valor global predeterminado o de cualquier política por origen almacenada. src/app/pages/privacy/privacy-page.component.html - 247,248 + 248,249 @@ -10827,7 +10855,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLos orígenes sensibles pueden realizar lecturas con Auto, pero las escrituras vuelven a exigir la autorización por origen para mutaciones antes de ejecutarse. Los orígenes no sensibles pueden excluirse o cambiarse a Ask desde la sección Consentimiento y auditoría del Panel de control. src/app/pages/privacy/privacy-page.component.html - 248,249 + 249,250 @@ -10835,7 +10863,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetCada llamada a una capacidad se registra en un registro de auditoría local redactado y de solo anexión. Nunca se almacenan argumentos, tokens, cookies ni cuerpos de respuesta. src/app/pages/privacy/privacy-page.component.html - 249,252 + 250,253 @@ -10843,7 +10871,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetCódigo abierto src/app/pages/privacy/privacy-page.component.html - 253,254 + 254,255 @@ -10851,7 +10879,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB es totalmente de código abierto bajo la licencia MIT. Puedes auditar cada línea de código para verificar estas afirmaciones sobre privacidad. El código fuente está disponible en GitHub. src/app/pages/privacy/privacy-page.component.html - 254,256 + 255,257 @@ -10859,7 +10887,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetCambios en esta política src/app/pages/privacy/privacy-page.component.html - 257,258 + 258,259 @@ -10867,7 +10895,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetSi se actualiza esta política, los cambios se reflejarán por la fecha de "Última actualización" en la parte superior de esta página. Los cambios significativos también se anotarán en las notas de versión del proyecto en GitHub. src/app/pages/privacy/privacy-page.component.html - 258,260 + 259,261 @@ -10875,7 +10903,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetContacto src/app/pages/privacy/privacy-page.component.html - 261,262 + 262,263 @@ -10883,7 +10911,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetSi tienes preguntas sobre esta política de privacidad o el tratamiento de datos de FSB, abre una incidencia en GitHub Issues. src/app/pages/privacy/privacy-page.component.html - 262,264 + 263,265 @@ -10891,7 +10919,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetHistorial de la política src/app/pages/privacy/privacy-page.component.html - 265,266 + 266,267 @@ -10899,7 +10927,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetCada entrada siguiente es una instantánea completa de la política de privacidad tal como estaba en la fecha indicada. Las versiones anteriores se conservan para que puedas auditar lo que prometimos en cualquier momento. src/app/pages/privacy/privacy-page.component.html - 266,268 + 267,269 @@ -11623,7 +11651,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetconduciendo el navegador src/app/pages/release-notes/release-notes-page.component.html - 176,180 + 176,179 @@ -11775,7 +11803,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetentrada desactivada · otra automatización posee esta pestaña src/app/pages/release-notes/release-notes-page.component.html - 226,231 + 226,230 @@ -12351,7 +12379,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetelemento src/app/pages/release-notes/release-notes-page.component.html - 408,412 + 408,411 @@ -12511,7 +12539,7 @@ https://full-selfbrowsing.com/api/telemetry/forget560 protegidos src/app/pages/release-notes/release-notes-page.component.html - 469,473 + 469,472 @@ -12671,7 +12699,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetnode 24 src/app/pages/release-notes/release-notes-page.component.html - 529,533 + 529,532 @@ -17999,7 +18027,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB - Notas de versión src/app/pages/release-notes/release-notes-page.component.ts - 88 + 86 @@ -18007,7 +18035,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetTodas las versiones de FSB, ilustradas: entornos de ejecución delegados para agentes, el icono de actividad de la barra, la propiedad de pestañas multiagente, el puente MCP y el catálogo de capacidades, hasta el primer prototipo de extensión para Chrome. src/app/pages/release-notes/release-notes-page.component.ts - 89 + 87 @@ -18179,11 +18207,11 @@ https://full-selfbrowsing.com/api/telemetry/forget - Where installs were last seen, by coarse region, over the past 365 days. - Dónde se vieron por última vez las instalaciones, por región aproximada, en los últimos 365 días. + Where installs were last seen over the past 365 days, by city, state, or country. Place data by DB-IP. + Dónde se vieron por última vez las instalaciones en los últimos 365 días, por ciudad, estado o país. Datos de ubicación de DB-IP. src/app/pages/stats/stats-page.component.html - 104,106 + 104,105 @@ -18239,7 +18267,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetError de red o de análisis. src/app/pages/stats/stats-page.component.ts - 132 + 133 @@ -18247,7 +18275,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetEstrellas src/app/pages/stats/stats-page.component.ts - 139 + 140 @@ -18255,7 +18283,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetConfirmaciones src/app/pages/stats/stats-page.component.ts - 140 + 141 @@ -18263,7 +18291,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetActivos ahora src/app/pages/stats/stats-page.component.ts - 141 + 142 @@ -18271,7 +18299,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetTókenes src/app/pages/stats/stats-page.component.ts - 142 + 143 @@ -18279,27 +18307,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetMás usados src/app/pages/stats/stats-page.component.ts - 143 - - - - Could not load chart library. - No se pudo cargar la biblioteca de gráficos. - - src/app/pages/stats/stats-page.component.ts - 183 - - - - Could not render the selected chart. - No se pudo renderizar el gráfico seleccionado. - - src/app/pages/stats/stats-page.component.ts - 190 - - - src/app/pages/stats/stats-page.component.ts - 862 + 144 @@ -18307,7 +18315,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLas estadísticas se están preparando; se reintentará en breve. src/app/pages/stats/stats-page.component.ts - 232 + 233 @@ -18315,7 +18323,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetA la respuesta de estadísticas le faltan los metadatos de actualidad. src/app/pages/stats/stats-page.component.ts - 235 + 236 @@ -18323,7 +18331,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLa última instantánea utilizable tiene más de 24 horas. src/app/pages/stats/stats-page.component.ts - 238 + 239 @@ -18331,7 +18339,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLa respuesta de estadísticas estaba mal formada. src/app/pages/stats/stats-page.component.ts - 241 + 242 @@ -18339,7 +18347,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLas estadísticas solo están disponibles en el navegador. src/app/pages/stats/stats-page.component.ts - 244 + 245 @@ -18347,7 +18355,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetEstrellas acumuladas del repositorio a lo largo del tiempo src/app/pages/stats/stats-page.component.ts - 252 + 253 @@ -18355,7 +18363,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetCommits acumulados del repositorio a lo largo del tiempo src/app/pages/stats/stats-page.component.ts - 254 + 255 @@ -18363,7 +18371,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetUso de tókenes de FSB durante los últimos 30 días src/app/pages/stats/stats-page.component.ts - 256 + 257 @@ -18371,7 +18379,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetProporción de clientes MCP registrados src/app/pages/stats/stats-page.component.ts - 258 + 259 @@ -18379,7 +18387,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetDesconocido src/app/pages/stats/stats-page.component.ts - 295 + 296 @@ -18387,15 +18395,15 @@ https://full-selfbrowsing.com/api/telemetry/forgetOtros src/app/pages/stats/stats-page.component.ts - 296 + 297 - Globe showing where FSB installs were last seen over the past 365 days, by coarse region - Globo que muestra dónde se vieron por última vez las instalaciones de FSB en los últimos 365 días, por región aproximada + Globe showing where FSB installs were last seen over the past 365 days, by city, state, or country + Globo que muestra dónde se vieron por última vez las instalaciones de FSB en los últimos 365 días, por ciudad, estado o país src/app/pages/stats/stats-page.component.ts - 308 + 309 @@ -18403,7 +18411,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetestrellas totales src/app/pages/stats/stats-page.component.ts - 317 + 318 @@ -18411,7 +18419,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetúltimos 7 días src/app/pages/stats/stats-page.component.ts - 318 + 319 @@ -18419,7 +18427,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetconfirmaciones totales src/app/pages/stats/stats-page.component.ts - 325 + 326 @@ -18427,7 +18435,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetúltimos 30 días src/app/pages/stats/stats-page.component.ts - 329 + 330 @@ -18435,7 +18443,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetagentes activos src/app/pages/stats/stats-page.component.ts - 343 + 344 @@ -18443,7 +18451,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetpromedio/usuario informante src/app/pages/stats/stats-page.component.ts - 347 + 348 @@ -18451,7 +18459,7 @@ https://full-selfbrowsing.com/api/telemetry/forgettókenes src/app/pages/stats/stats-page.component.ts - 357 + 358 @@ -18459,7 +18467,7 @@ https://full-selfbrowsing.com/api/telemetry/forgettókenes (24 h) src/app/pages/stats/stats-page.component.ts - 358 + 359 @@ -18467,7 +18475,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetclientes con seguimiento src/app/pages/stats/stats-page.component.ts - 364 + 365 @@ -18475,7 +18483,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetprincipal: src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18483,7 +18491,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetcliente principal src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18491,7 +18499,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetElige la vista de estadísticas. Vista actual: src/app/pages/stats/stats-page.component.ts - 402 + 403 @@ -18499,7 +18507,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB · Estadísticas src/app/pages/stats/stats-page.component.ts - 478 + 484 @@ -18507,7 +18515,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetIndicadores agregados en directo sobre la adopción, el uso y el repositorio de FSB. src/app/pages/stats/stats-page.component.ts - 486 + 492 @@ -18515,7 +18523,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetEstrellas acumuladas src/app/pages/stats/stats-page.component.ts - 974 + 979 @@ -18523,7 +18531,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetConfirmaciones acumuladas src/app/pages/stats/stats-page.component.ts - 995 + 1000 @@ -18531,7 +18539,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetTókenes (últimos 30 días) src/app/pages/stats/stats-page.component.ts - 1023 + 1028 @@ -18539,7 +18547,23 @@ https://full-selfbrowsing.com/api/telemetry/forgetClientes MCP populares src/app/pages/stats/stats-page.component.ts - 1046 + 1051 + + + + Could not load chart library. + No se pudo cargar la biblioteca de gráficos. + + src/app/pages/stats/stats-page.component.ts + 1105 + + + + Could not render the selected chart. + No se pudo renderizar el gráfico seleccionado. + + src/app/pages/stats/stats-page.component.ts + 1106 diff --git a/showcase/angular/src/locale/messages.ja.xlf b/showcase/angular/src/locale/messages.ja.xlf index 05f3eeb2..3133fd37 100644 --- a/showcase/angular/src/locale/messages.ja.xlf +++ b/showcase/angular/src/locale/messages.ja.xlf @@ -5515,7 +5515,7 @@ 最終スナップショット: src/app/pages/dashboard/dashboard-page.component.ts - 3994 + 4014 @@ -5523,7 +5523,7 @@ 状態: src/app/pages/dashboard/dashboard-page.component.ts - 3998 + 4018 @@ -5531,7 +5531,7 @@ 理由: src/app/pages/dashboard/dashboard-page.component.ts - 4002 + 4022 @@ -5539,7 +5539,7 @@ 復旧開始から 秒 src/app/pages/dashboard/dashboard-page.component.ts - 4006 + 4026 @@ -5547,7 +5547,7 @@ 最終フレーム: 秒前 src/app/pages/dashboard/dashboard-page.component.ts - 4012 + 4032 @@ -5555,7 +5555,7 @@ 適用済みミューテーション: src/app/pages/dashboard/dashboard-page.component.ts - 4013 + 4033 @@ -5563,7 +5563,7 @@ 適用失敗: src/app/pages/dashboard/dashboard-page.component.ts - 4014 + 4034 @@ -5571,7 +5571,7 @@ 古いミューテーション: src/app/pages/dashboard/dashboard-page.component.ts - 4015 + 4035 @@ -5579,7 +5579,7 @@ ストリームデータなし src/app/pages/dashboard/dashboard-page.component.ts - 4016 + 4036 @@ -5587,7 +5587,7 @@ ステータス でリクエストに失敗しました src/app/pages/dashboard/dashboard-page.component.ts - 4319 + 4339 @@ -5595,7 +5595,7 @@ 残り約分 src/app/pages/dashboard/dashboard-page.component.ts - 4474 + 4494 @@ -5603,7 +5603,7 @@ 残り約秒 src/app/pages/dashboard/dashboard-page.component.ts - 4475 + 4495 @@ -5611,7 +5611,7 @@ 実行時間: src/app/pages/dashboard/dashboard-page.component.ts - 4549 + 4569 @@ -5619,7 +5619,7 @@ ユーザーが停止しました — 直前の操作: src/app/pages/dashboard/dashboard-page.component.ts - 4554 + 4574 @@ -9542,12 +9542,32 @@ 307 + + September 2026v0.9.91 — 365-day region retention, Region (state-level) metric (full archived text) + 2026 年 9 月v0.9.91 — 地域情報の 365 日間保持、地域(州レベル)指標(アーカイブ全文) + + src/app/pages/privacy/privacy-history-archive.component.html + 3,6 + + + + Archived copy of the privacy policy as it stood from September 28, 2026, before the September 29, 2026 update.TLDR: FSB runs inside your browser, and no browsing data is collected by FSB servers. AI calls go directly from your browser to the provider you choose. API keys and credentials are encrypted locally, and memory data stays on your device. If you install the optional local agent service, FSB can also be driven by an MCP client running on your own machine over a loopback connection, and by coding-agent CLIs you have already installed. The only data that ever leaves your machine for FSB is opt-out anonymous usage telemetry that powers the public /stats page. If you pair Remote Dashboard, transient live-preview frames can pass through the relay during the session, but they are not stored. Everything is open source and auditable.Data CollectionFSB operates entirely within your browser. When you initiate an automation task, the extension may inspect the active tab's DOM (Document Object Model) and, when you invoke a capability, make same-origin site API requests from your browser session.No browsing history is collected or stored beyond the current sessionDOM data and site API results are analyzed locally and discarded after each automation step unless you explicitly save them in memoryNo personal information is harvested from pages you visitSite API capabilitiesFSB's capability layer can call a site's own first-party API from the page context, using your already-authenticated browser session. These requests run locally in Chrome; first-party cookies or site auth may be attached by the browser for that target site, but FSB does not return, store, log, or send cookies, tokens, CSRF values, request bodies, or response bodies to FSB servers.Capability invocations follow FSB's consent controls and are recorded only in the redacted local audit log: origin, capability slug, method, side-effect class, consent decision, and outcome. The audit log never stores invocation arguments, request bodies, response bodies, cookies, tokens, CSRF values, or site response payloads.Chrome permissions FSB requestsTo run web automation, FSB declares the following permissions in its Chrome manifest. Each is used only for the documented purpose; nothing is sent off-device on the strength of any of them.DOM and tabs, activeTab, scripting, tabs, windows, sidePanel, and host permission <all_urls>: read and write the active tab, inject the automation content script, list and switch tabs, and render the side panelAdvanced automation, debugger: attach the Chrome DevTools Protocol for coordinate-based clicks, drag, and key-hold actions that the regular DOM API cannot perform. webNavigation: observe navigation start/finish events so automation waits for the right momentLocal storage, storage, unlimitedStorage: store your settings, credentials, payment methods, and memory in chrome.storage.local on your device. Unlimited storage lifts the default 10 MB quota so memory and session logs can grow without hitting a wallUX helpers, clipboardWrite: write copy-to-clipboard results from automation. alarms: schedule background housekeeping. offscreen: host the speech-to-text recorder in a hidden document because service workers cannot capture audio directlyLocal service, nativeMessaging: start the optional local agent service described under Local Agent Service and Native Messaging below. Nothing about the pages you visit crosses that channel. system.memory: read the total installed RAM figure only, so FSB can suggest a sensible limit on how many agents run at once. The available-memory and per-processor breakdowns are never read, and the figure never leaves the extensionMicrophone access for speech-to-text is not declared in the manifest. Chrome shows its own permission prompt the first time you use the mic button.Data StorageAll settings and data are stored locally in Chrome's extension storage, with sensitive values encrypted at rest as described in the sections below.Configuration is stored in chrome.storage.localAPI keys, credentials, and payment methods are encrypted before storage, each detailed in its own section belowSession logs are stored locally and can be cleared at any timeAnalytics data (task counts, success rates) stays on your deviceExternal ServicesFSB communicates with external AI providers only when you configure and use a hosted provider. If you use LM Studio, AI requests stay on your machine through its local OpenAI-compatible server. The choice of provider and what data is sent is under your control.Hosted API calls are made only to the provider you select (xAI, OpenAI, Anthropic, Google, or OpenRouter)LM Studio uses a local OpenAI-compatible server on your device and does not require an API keySent data includes: task description, DOM structure summary, and action contextIf you pair Remote Dashboard or use legacy Background Agents sync, an optional relay server handles WebSocket messages for that session. Live-preview frames may pass through the relay transiently, but none of the following is ever persisted on an FSB server: page content, DOM data, browsing history, screenshots, AI prompts, AI responses, cookies, tokens, or site API payloads. This is opt-in only, and the rest of this policy refers back to this list rather than restating itEach provider has their own privacy policy governing how they handle API requestsLocal Agent Service and Native MessagingFSB can be driven by an MCP client running on your own machine. That requires a small local agent service which you install yourself, and the nativeMessaging permission exists solely so the extension can start it. Chrome cannot launch a local program directly, so this is the only supported mechanism.The messaging host io.github.fullselfbrowsing.fsb_native_host is registered only by an explicit action you take, by running fsb-mcp-server install --native-host. The extension cannot install it, and its registration names one permitted extension originThe exchange is a closed schema. FSB sends a version number, an action of either wake or bootstrap, and a locally generated correlation identifier; it receives that identifier back with an outcome and a reason drawn from a fixed list. Any additional field is rejected outright and messages are capped at 4 KB. No URL, tab, page content, or DOM data exists anywhere in this schemaA successful start returns a pairing code. This is a loopback authentication secret generated on your device and stored with owner-only permissions, rotated every time the service starts. It carries no information about you or your browsingThe service listens on your machine only. The extension bridge refuses to bind any address that is not loopback, and the MCP endpoint binds 127.0.0.1 by default and whenever FSB starts itUnder its own directory the service keeps only operational records: a diagnostics log of delegated-run outcomes restricted to a fixed field list, and a process-supervision journal. Task text, environment values, binary paths, and error text never reach either of them, and entries carrying credential-shaped values are discarded rather than writtenIf the host is not installed, the feature simply does not start. Nothing is transmitted, and the rest of the extension continues to work normallyDelegated CLI AgentsFSB can hand a task to a coding-agent command-line tool you have already installed, currently Claude Code or Grok Build. FSB starts that program on your own machine; your task is never proxied through FSB servers.Your task text reaches the tool over its standard input and nowhere else. FSB checks this before every run: if the task appears in the command line, the working directory, the environment, or any file path, the run is aborted rather than started. Tasks are capped at 64 KBWhile the run is in progress the tool calls back into FSB over the loopback connection to drive your browser. Whatever it reads that way — page text, DOM snapshots, spreadsheet values, screenshots — is returned to that tool, and therefore reaches its vendor: Anthropic for Claude Code, xAI for Grok BuildThe run uses the account already signed in to that tool. FSB does not hold, read, or transmit your Anthropic or xAI credential, and any usage or charges follow that vendor's own planGrok Build runs inside a profile directory FSB keeps separate from your own. The sign-in credential stored there is written by the Grok tool itself during its own sign-in flowThe agent instructions FSB supplies state that passwords, CVV values, payment-card data, and saved credentials must never enter prompts, narration, logs, or tool argumentsReading and Filling Google SheetsFSB can read and fill a range in Google Sheets by driving the spreadsheet interface in your own already-authenticated browser session.Reading a range returns those cell values to the model you selected, exactly like any other page content FSB reads on your behalf. Only the range you asked for is read; there is no whole-spreadsheet exportSpreadsheet content is stripped out before anything is written to FSB's local session history. Only shape counts survive — how many rows, columns, and values were involved. The spreadsheet address, sheet name, cell references, and every cell value are discardedIf that filtering cannot be applied to a record for any reason, the record is dropped rather than storedRemote Dashboard and PhantomStream Live PreviewWhen you pair Remote Dashboard, FSB can show a live preview of the active browser tab using PhantomStream. The preview streams structured DOM data instead of pixels: an initial snapshot, MutationObserver diffs, scroll position, automation overlays, dialog state, media playback state, and remote-control messages over WebSocket.The relay forwards those live-preview frames only to the paired dashboard session, and nothing from the preview stream is persisted on FSB servers — see the list under External Services aboveFSB enables PhantomStream input masking (maskInputs: true), so passwords and form-control values are masked before they leave the captured pageThe dashboard viewer renders mirrored content in a scriptless sandbox and sanitizes mirrored DOM and CSS before displayImages, video, and audio are mirrored by reference in current dashboard mode. Media bytes do not cross the relay; the viewer may fetch allowed public HTTPS asset or media URLs directly, while private, internal, non-HTTPS, or otherwise blocked origins are replaced with placeholders by PhantomStream's fail-closed fetch policyNo Third-Party TrackingFSB does not include any third-party analytics, ad trackers, or cross-site fingerprinting. There are no cookies and no third-party scripts beyond the AI provider APIs you explicitly configure. The one piece of first-party data FSB sends home is the opt-out Anonymous Usage Telemetry described below, used solely to power the public /stats dashboard.API KeysYour API keys are encrypted locally using AES-GCM before being stored. They are never transmitted anywhere except to the AI provider you configured, and only as authentication headers in API requests.Keys are encrypted at rest in Chrome storageDecryption only happens in-memory when making API callsKeys are never logged, exported, or sharedAuto-PasswordsFSB includes an optional credential manager that stores login credentials encrypted on your device. Passwords are never exposed to AI models. They are filled directly into pages by the content script, bypassing the AI entirely.Credentials are encrypted at rest using AES-GCM with 256-bit keys and PBKDF2 key derivationWhen the AI analyzes a page, password field values are replaced with [hidden]. The actual password is never included in any AI promptAuto-fill is performed by the content script injecting values directly into the DOM, with no AI involvement in the credential flowThe credential list view only shows usernames and domains. Passwords are decrypted individually and only when needed for auto-fillCredentials are stored per-domain with parent domain fallback (e.g., accounts.google.com inherits from google.com)Payment MethodsFSB includes an optional payment-method vault that stores card details on your device for checkout auto-fill. Cards are treated with the same encryption and AI isolation as login credentials, and the full card number is never sent to any AI model.Card details (number, expiry, cardholder, and zip) are encrypted at rest using AES-GCM with the same vault-derived key used for credentialsWhen the AI analyzes a checkout page, any detected card-number field values are replaced with [hidden] before the prompt is built. Card numbers, CVV, and expiry are never included in any AI promptAuto-fill happens via the content script writing directly into the page's DOM fields, bypassing the AI entirelyThe list view shows only a card nickname and last-4 digits. Full numbers are decrypted in memory only at the moment of fillAn MCP client can request a payment fill via use_payment_method, but the user is shown an in-extension confirmation prompt before any card data is written into the pageCVV is never persisted unless you opt in per-card, and even then it is encrypted alongside the rest of the recordSpeech-to-TextFSB includes an optional microphone input for the prompt box. The default provider runs entirely in your browser; an optional OpenAI Whisper fallback can be enabled in settings if you want higher accuracy.Default provider: the browser's native SpeechRecognition API. Audio is processed by Chrome and never leaves your device through FSBOptional Whisper provider: when sttProvider is set to whisper and an OpenAI key is configured, recorded audio chunks are uploaded directly from your browser to OpenAI's transcription endpoint. FSB never sees or stores the audioThe microphone is only active while you are holding or have toggled the mic button. Chrome prompts for permission the first time you use it; FSB does not request microphone access in the extension manifestTranscripts are inserted into the prompt textarea only and are never logged, persisted, or transmitted outside the active AI request you choose to sendDisable speech entirely by leaving the mic button untouched, or by clearing the optional Whisper provider in Chrome extension storagePrompt Injection PreventionWeb pages can contain hidden text designed to hijack AI agents. FSB implements multi-layered defenses to ensure the AI only follows your instructions, never instructions embedded in page content.All page content is wrapped in [PAGE_CONTENT] boundary markers, and the AI is instructed to never follow instructions found within these markersA sanitization engine strips known injection patterns (e.g., "ignore previous instructions", fake system prompts, override attempts) from all page content before it reaches the AIAI-generated actions are validated before execution. Dangerous URLs (javascript:, data:) and script injection attempts are blockedOnly a strict, fixed allowlist of known tools can be executed. The AI cannot invent or call arbitrary actionsContent size is capped (500 chars per value, 15K total prompt cap) to limit payload deliveryInvisible Unicode control characters that websites embed are stripped before processingBackground Agents and Server SyncDeprecated in v0.9.45rc1. FSB's built-in Background Agents have been superseded by OpenClaw and Claude Routines, with remote control now handled by the Sync tab. The disclosures below are retained for users still running v0.9.44 or earlier; on current builds the relay server is only contacted when you pair a Sync session.If you opt into legacy Background Agents server sync or pair Remote Dashboard, a relay server facilitates communication between your extension and the dashboard.The server stores: agent definitions (name, schedule, target URL), run metrics (token count, cost, duration, success/fail status), and session pairing tokensBeyond that run metadata the server persists nothing derived from the pages you visit — see the list under External Services aboveAuthentication uses hash keys (generated locally) and session tokens that expire after 24 hoursOne-time pairing tokens expire after 60 seconds and cannot be reusedServer sync is disabled by default. You must explicitly enable it in OptionsMemory SystemFSB's memory system stores navigation patterns and site intelligence to improve automation over time.All memory data (semantic, episodic, procedural) is stored locally in chrome.storage.localNo memory data is sent to any external serverMemory can be viewed and cleared at any time from the Options dashboardSite maps and navigation patterns are domain-specific and isolated from each otherSession Replay and ScreenshotsWhen an MCP client drives FSB, the extension records what the agent did so you can review or replay the run afterwards. These records stay on your device.Runs still in progress are held in Chrome's session storage so they survive the service worker being evicted; finished runs are written to chrome.storage.local through the same history store your own automation runs useRecords are redacted before they are stored. Values under credential-shaped keys, text typed into fields that look like password, one-time-code, or card inputs, and signed or token-bearing URL parameters are all replaced. Ordinary addresses and selectors are preserved, because replay does not work without themRecorded runs are kept for 30 days by default, adjustable between 1 and 365 days, and older ones are pruned by a daily job. Recording can be switched off entirely in Advanced SettingsScreenshot images are never written into that history. Only capture metadata such as dimensions and byte size is retainedScreenshots an MCP client requests are saved by the local service as files on your own disk with owner-only permissions and deleted automatically after seven days. The image is also handed back to the requesting client, and therefore to that client's modelAnonymous Usage TelemetryFSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.What we collectA random per-install UUID stored in chrome.storage.local under the key fsbInstallUuid. The UUID is generated locally and never tied to your identity.The name of the MCP client used (e.g. Claude Code, Cursor, Codex), drawn from a fixed allowlist.The model name used for a session (e.g. grok-4-fast, claude-opus-4), drawn from a fixed allowlist.Aggregate input/output token counts per session.The number of active FSB agents on your install (an integer count).What we do NOT collectPage URLs, hostnames, or browsing history.Prompts, instructions, task descriptions, or any natural-language text you send to your model provider.Page DOM, screenshots, page content, site API payloads, or AI responses.Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.Names, usernames, account handles, or any free-form identity fields.Email addresses, phone numbers, or contact information.Region (state-level) metricThe server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs.The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).RetentionRaw events are retained for 7 days. Daily rollups (one row per install per day, including the coarse region label) are retained for 365 days. Global aggregates (one row per day, no per-install dimension) are retained indefinitely so historical charts on /stats remain stable.How to opt outOpen the FSB Control Panel, scroll to Advanced Settings, and toggle Send anonymous usage data off. The change takes effect immediately; no further events will be sent from your install.How to erase your dataTo request erasure of all telemetry rows associated with your install (GDPR Article 17), look up your fsbInstallUuid in Chrome DevTools → Application → Storage → Extension storage, then send a single HTTP request:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Consent and Audit ControlsFSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. + 2026年9月29日の更新前、2026年9月28日以降のプライバシーポリシーのアーカイブです。要約: FSB はブラウザ内で動作し、FSB のサーバーが閲覧データを収集することはありません。AI 呼び出しはブラウザから選択したプロバイダーへ直接送信されます。API キーと認証情報はローカルで暗号化され、メモリーデータは端末内に留まります。任意のローカルエージェントサービスをインストールすると、FSB は自分のマシン上でループバック接続を介して動作する MCP クライアントや、すでにインストール済みのコーディングエージェント CLIs からも操作できます。FSB のためにマシンから送信される唯一のデータは、公開ページ /stats を支えるオプトアウト可能な匿名利用テレメトリだけです。Remote Dashboard をペアリングすると、一時的なライブプレビューのフレームがセッション中にリレーを経由することがありますが、保存されることはありません。すべてオープンソースで検証可能です。データ収集FSB はブラウザ内だけで動作します。自動化タスクを開始すると、拡張機能はアクティブなタブの DOM(Document Object Model)を調べる場合があります。また、ケイパビリティを呼び出すと、ブラウザセッションから同一オリジンのサイト API リクエストを実行する場合があります。現在のセッションを超えて閲覧履歴を収集・保存しませんDOM データとサイト API の結果はローカルで解析され、明示的にメモリへ保存しない限り、各自動化ステップ後に破棄されます訪問先ページから個人情報を抽出しませんサイト API 機能FSB のケイパビリティ層は、認証済みのブラウザセッションを使って、ページコンテキストからサイト自身のファーストパーティ API を呼び出せます。リクエストは Chrome 内でローカル実行され、対象サイトのファーストパーティ Cookie やサイト認証情報がブラウザによって付与される場合があります。ただし FSB は Cookie、トークン、CSRF 値、リクエスト本文、レスポンス本文を返却、保存、記録したり、FSB のサーバーへ送信したりしません。ケイパビリティ呼び出しは FSB の同意制御に従い、オリジン、ケイパビリティスラッグ、メソッド、副作用区分、同意判断、結果だけが秘匿化済みのローカル監査ログに記録されます。監査ログに呼び出し引数、リクエスト本文、レスポンス本文、Cookie、トークン、CSRF 値、サイトのレスポンス内容が保存されることはありません。Chrome がリクエストする FSB パーミッションウェブ自動化を実行するため、FSB は Chrome マニフェストで以下のパーミッションを宣言します。それぞれは記載された目的のためにのみ使用され、いずれを根拠としてもデータが端末外に送信されることはありません。DOM とタブ、activeTab、scripting、tabs、windows、sidePanel、ホスト権限 <all_urls>:アクティブなタブの読み書き、自動化コンテンツスクリプトの挿入、タブの一覧表示と切り替え、サイドパネルの描画高度な自動化、debugger:Chrome DevTools Protocol を接続し、通常の DOM API では実行できない座標指定クリック、ドラッグ、キー長押し操作を可能にします。webNavigation:ナビゲーションの開始・完了イベントを監視し、自動化が適切なタイミングまで待機ローカルストレージ、storage、unlimitedStorage:設定、認証情報、支払い方法、メモリをデバイス上の chrome.storage.local に保存。無制限ストレージにより既定の 10 MB 上限が解除され、メモリやセッションログを容量不足なしで増やせますUX ヘルパー、clipboardWrite:自動化結果をクリップボードへ書き込み。alarms:バックグラウンドの定期処理をスケジュール。offscreen:Service Worker は音声を直接取得できないため、非表示ドキュメントで音声テキスト変換レコーダーを実行ローカルサービス, nativeMessaging: 下記の ローカルエージェントサービスとネイティブメッセージング で説明する任意のローカルエージェントサービスを起動します。閲覧したページに関する情報がこのチャネルを通ることはありません。system.memory: 搭載されている総 RAM 容量のみを読み取り、FSB が同時に実行するエージェント数の妥当な上限を提案できるようにします。空きメモリーやプロセッサーごとの内訳が読み取られることはなく、この値が拡張機能の外に出ることもありません音声認識用のマイクアクセスはマニフェストで宣言されていません。マイクボタンを初めて使用したときに Chrome 自身が許可プロンプトを表示します。データ保存すべての設定とデータは Chrome の拡張機能ストレージにローカル保存され、機微な値は以下の各セクションで説明するとおり保存時に暗号化されます。設定は chrome.storage.local に保存されますAPI キー、認証情報、支払い方法は保存前に暗号化されます。詳細はそれぞれ下記の該当セクションに記載していますセッションログはローカル保存され、いつでも削除できます分析データ (タスク数、成功率) は端末上に残ります外部サービスFSB はホスト型プロバイダーを設定して利用する場合にのみ、外部 AI プロバイダーと通信します。LM Studio を使う場合、AI リクエストはローカルの OpenAI 互換サーバーを介して端末内にとどまります。プロバイダーの選択と送信するデータはユーザーの管理下にあります。ホスト API への呼び出しは、選択したプロバイダー (xAI、OpenAI、Anthropic、Google、または OpenRouter) にのみ送信されますLM Studio は端末上のローカル OpenAI 互換サーバーを使用し、API キーは不要です送信データ:タスクの説明、DOM 構造の要約、操作コンテキストRemote Dashboard をペアリングした場合、または従来の Background Agents 同期を使用する場合、オプションのリレーサーバーがそのセッションの WebSocket メッセージを処理します。ライブプレビューのフレームが一時的にリレーを経由することはありますが、以下のいずれも FSB のサーバーに保存されることはありません: ページ内容、DOM データ、閲覧履歴、スクリーンショット、AI プロンプト、AI 応答、Cookie、トークン、サイトの API ペイロード。これはオプトインのみで、本ポリシーの他の箇所はこの一覧を繰り返さずに参照します各プロバイダーは、API リクエストの取り扱いを定める独自のプライバシーポリシーを有しますローカルエージェントサービスとネイティブメッセージングFSB は自分のマシン上で動作する MCP クライアントから操作できます。そのためには自分でインストールする小さなローカルエージェントサービスが必要で、nativeMessaging 権限は拡張機能がそれを起動するためだけに存在します。Chrome はローカルプログラムを直接起動できないため、これが唯一のサポートされた仕組みです。メッセージングホスト io.github.fullselfbrowsing.fsb_native_host は、あなたが fsb-mcp-server install --native-host を実行するという明示的な操作によってのみ登録されます。拡張機能がインストールすることはできず、その登録には許可された拡張機能のオリジンが 1 つだけ記載されますこのやり取りは閉じたスキーマです。FSB はバージョン番号、wake または bootstrap のいずれかのアクション、およびローカルで生成した相関 ID を送信し、その ID と、固定リストから選ばれた結果と理由を受け取ります。追加のフィールドは即座に拒否され、メッセージは 4 KB に制限されます。このスキーマには URL、タブ、ページ内容、DOM データのいずれも存在しません起動に成功するとペアリングコードが返されます。これは端末上で生成され、所有者のみがアクセスできる権限で保存され、サービスが起動するたびに更新されるループバック認証シークレットです。あなた自身や閲覧内容に関する情報は一切含みませんこのサービスは自分のマシン上でのみ待ち受けます。拡張機能ブリッジはループバック以外のアドレスへのバインドを拒否し、MCP エンドポイントは既定で、また 127.0.0.1 が起動する場合は常に FSB にバインドしますこのサービスは自身のディレクトリに運用記録のみを保持します: 固定のフィールド一覧に限定された委任実行結果の診断ログと、プロセス監視ジャーナルです。タスクテキスト、環境変数の値、バイナリのパス、エラーテキストがどちらにも記録されることはなく、認証情報らしき値を含むエントリは書き込まれずに破棄されますホストがインストールされていない場合、この機能は単に起動しません。何も送信されず、拡張機能の他の部分はこれまでどおり動作します委任された CLI エージェントFSB は、すでにインストール済みのコーディングエージェントのコマンドラインツール(現在は Claude Code または Grok Build)にタスクを引き渡せます。FSB はそのプログラムを自分のマシン上で起動します。タスクが FSB のサーバーを経由することはありません。タスクのテキストは標準入力経由でのみツールに渡され、それ以外の経路はありません。FSB は実行のたびにこれを検証し、タスクがコマンドライン、作業ディレクトリ、環境変数、いずれかのファイルパスに現れた場合は、実行を開始せずに中止します。タスクの上限は 64 KB です実行中、ツールはループバック接続を介して FSB を呼び出し、ブラウザを操作します。その過程で読み取ったもの、すなわちページテキスト、DOM スナップショット、スプレッドシートの値、スクリーンショットはそのツールに返され、したがってその提供元に渡ります。対応は Anthropic が Claude Code、xAI が Grok Build実行にはそのツールで既にサインイン済みのアカウントが使われます。FSB があなたの Anthropic や xAI の認証情報を保持・読み取り・送信することはなく、利用量や料金はその提供元のプランに従いますGrok Build は、FSB があなた自身のものとは分離して管理するプロファイルディレクトリ内で実行されます。そこに保存されるサインイン認証情報は、Grok ツール自身がそのサインインフローの中で書き込みますFSB が提供するエージェント指示は、パスワード、CVV の値、支払いカード情報、保存済み認証情報がプロンプト、説明文、ログ、ツール引数に決して入ってはならないと定めていますGoogle Sheets の読み取りと入力FSB は、既に認証済みのブラウザセッション内でスプレッドシートの画面を操作することにより、Google Sheets の範囲を読み取ったり入力したりできます。範囲を読み取ると、それらのセルの値は、FSB があなたの代わりに読み取る他のページ内容とまったく同様に、選択したモデルへ送信されます。読み取られるのは指定した範囲のみで、スプレッドシート全体がエクスポートされることはありませんスプレッドシートの内容は、FSB のローカルセッション履歴に何かが書き込まれる前に取り除かれます。残るのは規模を示す件数(行数、列数、値の数)だけです。スプレッドシートのアドレス、シート名、セル参照、すべてのセルの値は破棄されます何らかの理由でこのフィルタリングをレコードに適用できない場合、そのレコードは保存されずに破棄されますRemote Dashboard と PhantomStream のライブプレビューRemote Dashboard とペアリングすると、FSB は PhantomStream を使ってアクティブなブラウザタブをライブプレビューできます。プレビューではピクセルではなく構造化された DOM データをストリーミングします。初期スナップショット、MutationObserver 差分、スクロール位置、自動化オーバーレイ、ダイアログ状態、メディア再生状態、WebSocket 経由のリモート操作メッセージが含まれます。リレーはこれらのライブプレビューのフレームをペアリング済みのダッシュボードセッションにのみ転送し、プレビューストリームの内容が FSB のサーバーに保存されることはありません。上記の 外部サービスの一覧 を参照してくださいFSB は PhantomStream の入力マスキング(maskInputs: true)を有効にするため、パスワードとフォームコントロールの値はキャプチャしたページを離れる前にマスクされますダッシュボードのビューアーはスクリプトを実行できないサンドボックス内でミラー内容を描画し、表示前にミラーされた DOM と CSS をサニタイズします現在のダッシュボードモードでは、画像、動画、音声を参照としてミラーします。メディアのバイト列はリレーを通過しません。ビューアーは許可された公開 HTTPS アセットまたはメディア URL を直接取得できますが、プライベート、内部、非 HTTPS、その他ブロック対象のオリジンは、PhantomStream のフェイルクローズ取得ポリシーによってプレースホルダーに置き換えられますサードパーティトラッキングなしFSB はサードパーティのアナリティクス、広告トラッカー、クロスサイトフィンガープリンティングを一切含みません。Cookie もなく、明示的に設定した AI プロバイダーの APIs 以外のサードパーティスクリプトもありません。FSB が送信するファーストパーティデータは、以下に説明するオプトアウト式の匿名利用テレメトリのみであり、これは公開 /stats ダッシュボードの提供のみに使用されます。API キーAPI キーは保存前にローカルで AES-GCM で暗号化されます。設定した AI プロバイダー以外には送信されず、送信時も API リクエストの認証ヘッダーとしてのみ使われます。キーは Chrome ストレージ上で暗号化された状態で保管されます復号は API 呼び出し時にメモリ内でのみ行われますキーは記録、エクスポート、共有のいずれもされません自動パスワード入力FSB はログイン情報を端末上で暗号化保存するオプションの認証情報マネージャーを含みます。パスワードは AI モデルに渡されることはなく、AI を介さずに Content Script が直接ページに入力します。認証情報は 256 ビット鍵と AES-GCM 鍵導出による PBKDF2 で保存時に暗号化されますAI がページを解析する際、パスワードフィールドの値は [hidden] に置き換えられます。実際のパスワードが AI プロンプトに含まれることはありません自動入力はコンテンツスクリプトが値を直接 DOM へ挿入して行い、認証情報の処理に AI は一切関与しません認証情報の一覧表示にはユーザー名とドメインのみが表示されます。パスワードは自動入力に必要なときにのみ個別に復号されます認証情報はドメイン単位で保存され、親ドメインへのフォールバックを行います (例: accounts.google.com は google.com を継承)決済方法FSB には、決済時の自動入力用にカード情報をお使いの端末に保存するオプションの決済方法ボールトが含まれています。カードはログイン認証情報と同じ暗号化と AI 隔離で扱われ、完全なカード番号は決してどの AI モデルにも送信されません。カード情報(番号、有効期限、カード名義人、郵便番号)は、認証情報と同じボールト由来のキーを使用して AES-GCM で保管時に暗号化されますAI が決済ページを解析する際、検出されたカード番号フィールドの値はプロンプトの組み立て前に [hidden] に置き換えられます。カード番号、CVV、有効期限はいかなる AI プロンプトにも含まれません自動入力はコンテンツスクリプトがページの DOM フィールドに直接書き込むことで行われ、AI を完全に経由しませんリスト表示ではカードのニックネームと下4桁のみが表示されます。完全な番号は入力の瞬間のみ、メモリー内でのみ復号されますMCP クライアントは use_payment_method 経由で決済の自動入力をリクエストできますが、カード情報がページに書き込まれる前に、拡張機能内で確認プロンプトがユーザーに表示されますカードごとに明示的にオプトインしない限り、CVV は決して保存されません。保存される場合でも、他のレコードと一緒に暗号化されます音声認識(Speech-to-Text)FSB にはプロンプト入力欄のオプションのマイク入力が含まれています。既定のプロバイダーはブラウザ内で完全に動作します。より高い精度が必要な場合は、設定でオプションの OpenAI Whisper フォールバックを有効にできます。既定のプロバイダー:ブラウザ標準の SpeechRecognition API。音声は Chrome で処理され、FSB を通じて端末外へ送信されることはありませんオプションの Whisper プロバイダー: sttProvider が whisper に設定されており、OpenAI キーが設定されている場合、録音された音声チャンクはお使いのブラウザから直接 OpenAI の文字起こしエンドポイントにアップロードされます。FSB がその音声を見たり保存したりすることはありませんマイクはマイクボタンを押し続けている、または切り替えている間だけ作動します。初回使用時に Chrome が許可を求めます。FSB は拡張機能マニフェストでマイクアクセスをリクエストしません文字起こしはプロンプトのテキストエリアにのみ挿入され、お客様が送信する有効な AI リクエスト以外に記録、保存、または送信されることは決してありませんマイクボタンを触らないか、Chrome の拡張機能ストレージからオプションの Whisper プロバイダーをクリアすることで、音声機能を完全に無効化できますプロンプトインジェクション対策Web ページには AI エージェントを乗っ取るための隠しテキストが含まれることがあります。FSB は多層防御を実装し、AI が指示するのは常にユーザーの指示のみで、ページコンテンツに埋め込まれた指示には従わないようにします。すべてのページコンテンツは [PAGE_CONTENT] 境界マーカーで囲まれ、AI はその内側にある指示には絶対に従わないよう指示されますサニタイズエンジンが既知のインジェクションパターン(例:「以前の指示を無視」、偽のシステムプロンプト、上書き試行)を、AI に渡る前にページ内容から取り除きますAI が生成したアクションは実行前に検証されます。危険な URL (javascript:、data:) やスクリプト挿入の試みはブロックされます実行できるのは、厳格に固定された許可リスト内の既知ツールだけです。AI が任意の操作を作成したり呼び出したりすることはできませんコンテンツサイズには上限があり (値あたり 500 文字、プロンプト全体 15K)、ペイロードの送り込みを制限しますWeb サイトが埋め込む不可視の Unicode 制御文字は処理前に取り除かれますバックグラウンドエージェントとサーバー同期v0.9.45rc1 で非推奨。 FSB の組み込みバックグラウンドエージェントは OpenClaw と Claude Routines に置き換えられ、リモート操作は現在 Sync タブが担っています。以下の開示は v0.9.44 以前を使用するユーザー向けに残しています。現在のビルドでリレーサーバーへ接続するのは、Sync セッションをペアリングした場合だけです。従来のバックグラウンドエージェントのサーバー同期をオプトインで使用するか、Remote Dashboard とペアリングすると、リレーサーバーが拡張機能とダッシュボード間の通信を仲介します。サーバーが保存するのは、エージェント定義 (名前、スケジュール、対象 URL)、実行メトリクス (トークン数、コスト、所要時間、成功/失敗ステータス)、セッションペアリングトークンですその実行メタデータ以外に、閲覧したページに由来するものをサーバーが保存することはありません。上記の 外部サービスの一覧 を参照してください認証にはローカル生成のハッシュ鍵と、24 時間で失効するセッショントークンを使用しますワンタイムペアリングトークンは 60 秒で失効し、再利用できませんサーバー同期はデフォルトで無効です。オプションから明示的に有効化する必要がありますメモリーシステムFSB のメモリーシステムは、自動化を時とともに改善するためにナビゲーションパターンとサイトインテリジェンスを蓄積します。すべてのメモリーデータ (意味、エピソード、手続き) は chrome.storage.local にローカル保存されますメモリーデータが外部サーバーに送信されることはありませんメモリーはオプションダッシュボードからいつでも参照・削除できますサイトマップとナビゲーションパターンはドメイン単位で、互いに分離されますセッションリプレイとスクリーンショットMCP クライアントが FSB を操作すると、拡張機能はエージェントの動作を記録し、後から実行内容を確認したり再生したりできるようにします。これらの記録は端末内に留まります。進行中の実行は Chrome のセッションストレージに保持され、Service Worker が破棄されても失われません。完了した実行は chrome.storage.local を通じて、自分の自動化実行と同じ履歴ストアに書き込まれます記録は保存前に秘匿処理されます。認証情報らしきキーの下にある値、パスワード・ワンタイムコード・カード入力とみられるフィールドに入力されたテキスト、署名付きやトークンを含む URL パラメーターはすべて置き換えられます。通常のアドレスやセレクターは、それがないとリプレイが成立しないため保持されます記録された実行は既定で 30 日間保持され、1 日から 365 日の範囲で調整でき、古いものは日次ジョブによって削除されます。記録は詳細設定で完全に無効化できますスクリーンショットの画像がこの履歴に書き込まれることはありません。保持されるのは寸法やバイトサイズといったキャプチャのメタデータだけですMCP クライアントが要求したスクリーンショットは、ローカルサービスが所有者のみアクセスできる権限で自分のディスク上にファイルとして保存し、7 日後に自動削除します。画像は要求元のクライアントにも返されるため、そのクライアントのモデルにも渡ります匿名利用テレメトリーFSB v0.9.69 では、プロジェクトが集計された導入数を公開できるよう、オプトアウト可能な匿名利用テレメトリパイプラインを導入しました(/stats を参照)。閲覧中のページに触れることは一切ありません。唯一の位置情報シグナルは、サーバーが取り込み時にリクエストの IP から導出する大まかな国/州ラベルであり(ページ内容からではありません)、集計形式でのみ公開されます。詳しくは下記の 地域(州レベル)指標 を参照してください。テレメトリは既定で有効ですが、1 つのトグルで無効化でき、インストールごとのデータはリクエストにより消去できます。収集する情報インストールごとにランダムな UUID を生成し、chrome.storage.local のキー fsbInstallUuid に保存します。UUID はローカルで生成され、あなたの身元には決して紐付けられません。使用された MCP クライアントの名前 (例: Claude Code、Cursor、Codex)。固定の許可リストから取得します。セッションで使用されたモデル名 (例: grok-4-fast、claude-opus-4)。固定の許可リストから取得します。セッションごとの入出力トークン数の集計値。お使いのインストールでアクティブな FSB エージェントの数 (整数値)。収集しない情報ページの URLs、ホスト名、または閲覧履歴。プロンプト、指示、タスクの説明、またはモデルプロバイダーに送信する自然言語テキスト。ページの DOM、スクリーンショット、ページ内容、サイト API ペイロード、AI レスポンス。平文の IP アドレス。サーバーはリクエストの IP を、ちょうど 3 つの目的(レート制限のための日次ローテーションソルトによるハッシュ、大まかな国/州ラベルの導出(下記参照)、およびアドレスが IPv4 と IPv6 のどちらであるかの記録)のために一時的かつインラインで使用し、その後直ちに破棄します。平文の IP が保存またはログ記録されることは決してありません。氏名、ユーザー名、アカウントハンドル、または自由記述のあらゆる身元フィールド。メールアドレス、電話番号、または連絡先情報。地域(州レベル)指標サーバーは取り込み時に、自己ホストの IP DB-IP-to-City Lite データセットと独自のルックアップを用いて、リクエストの IP から大まかな国および米国の州(サブディビジョン)ラベルを導出します。これはライブのサードパーティ地理位置情報サービスではなく、MaxMind でもありません。平文の IP はインラインで処理されて破棄され、導出されたラベルとアドレスファミリー(IPv4 または IPv6)のみが保持されます。地域は集計形式でのみ、k≥5 の匿名性しきい値の背後で公開されます。固有インストール数が 5 未満の州はすべて単一の「Other」バケットにまとめられます(その合計数自体が 5 未満の場合は完全に抑制されます)。公開される州ラベルが 5 未満のインストールを表すことは決してありません。大まかなラベルは、生イベント(7 日間の保持期間で削除)と、インストールの日次ロールアップに保存されます。日次ロールアップは、/stats が各インストールを最新の地域で 1 回だけ数えられるよう 365 日間保持されます。このラベルが IP アドレス、都市、座標であることはありません。消去をリクエストすると、インストールごとの他のデータとともに削除され、上記の k≥5 しきい値の背後でのみ公開されます。地理位置情報データは DB-IP(https://db-ip.com)によります。保持期間生イベントは 7 日間保持されます。日次ロールアップ (インストールごと、日ごとに 1 行。大まかな地域ラベルを含む) は 365 日間保持されます。グローバル集計 (日ごとに 1 行、インストール次元なし) は /stats の履歴チャートが安定するよう無期限に保持されます。オプトアウトの方法「FSB コントロールパネル」を開き、「詳細設定」までスクロールして、匿名利用データを送信 をオフにしてください。変更は直ちに反映され、それ以降このインストール環境からイベントは送信されません。データを消去する方法インストールに関連するすべてのテレメトリ行の消去を依頼するには(GDPR 第 17 条)、fsbInstallUuid を Chrome DevTools → アプリケーション → ストレージ → 拡張機能ストレージ で確認し、次の HTTP リクエストを 1 回送信します:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use への準拠表明FSB の匿名利用テレメトリーは、full-selfbrowsing.com/stats で公開される集計利用統計の算出にのみ使用されます。データは決して販売されず、第三者と共有されず、広告のために使用されず、機械学習モデルの学習にも使用されません。この方針は Chrome Web Store の Limited Use 要件を満たします。集計された公開メトリクスこのテレメトリーパイプラインから導出された集計メトリクスを /stats で公開しています。表示されるのは件数と合計のみで、インストールごとの行は決して公開されません。同意と監査の制御FSB の自動化方針はオプトアウト式です。拒否リスト外のオリジンは、現在「自動」で提供されるユーザー設定可能な全体既定値を継承し、オリジンごとの明示的なポリシーでは「オフ」「確認」「自動」を設定できます。監査ログはローカルに期限付きで保持され、コントロールパネルからエクスポートと消去を行えます。サービス拒否リストは引き続き強制ブロックです。拒否対象のオリジンは、全体既定値や保存済みのオリジン別ポリシーに関係なく有効化できません。機密性の高いオリジンでは「自動」で読み取りを実行できますが、書き込み前にはオリジン別の変更許可を再確認します。機密性の低いオリジンはオプトアウトするか、コントロールパネルの「同意と監査」セクションで「確認」に変更できます。すべてのケイパビリティ呼び出しは、墨消しされた追記専用のローカル監査ログに記録されます。引数、トークン、Cookie、レスポンスボディが保存されることは決してありません。オープンソースFSB は MIT ライセンスの下で完全にオープンソースです。プライバシーに関する主張はコードを 1 行ずつ監査して確認できます。ソースコードは GitHub で公開されています。本ポリシーの変更本ポリシーが更新された場合、ページ上部の「最終更新」日に反映されます。重大な変更は、プロジェクトの GitHub リリースノートにも記載されます。お問い合わせこのプライバシーポリシーや FSB のデータ取り扱いについて質問がある場合は、GitHub Issues で課題を作成してください。 + + src/app/pages/privacy/privacy-history-archive.component.html + 7,238 + + August 2026v0.9.91 — Local Agent Service and Native Messaging, Delegated CLI Agents, Google Sheets, Session Replay and Screenshots (full archived text) 2026 年 8 月v0.9.91 — ローカルエージェントサービスとネイティブメッセージング、委任された CLI エージェント、Google Sheets、セッションリプレイとスクリーンショット(アーカイブ全文) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 242,245 @@ -9559,7 +9579,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use への準拠表明FSB の匿名利用テレメトリーは、full-selfbrowsing.com/stats で公開される集計利用統計の算出にのみ使用されます。データは決して販売されず、第三者と共有されず、広告のために使用されず、機械学習モデルの学習にも使用されません。この方針は Chrome Web Store の Limited Use 要件を満たします。集計された公開メトリクスこのテレメトリーパイプラインから導出された集計メトリクスを /stats で公開しています。表示されるのは件数と合計のみで、インストールごとの行は決して公開されません。同意と監査の制御FSB の自動化方針はオプトアウト式です。拒否リスト外のオリジンは、現在「自動」で提供されるユーザー設定可能な全体既定値を継承し、オリジンごとの明示的なポリシーでは「オフ」「確認」「自動」を設定できます。監査ログはローカルに期限付きで保持され、コントロールパネルからエクスポートと消去を行えます。サービス拒否リストは引き続き強制ブロックです。拒否対象のオリジンは、全体既定値や保存済みのオリジン別ポリシーに関係なく有効化できません。機密性の高いオリジンでは「自動」で読み取りを実行できますが、書き込み前にはオリジン別の変更許可を再確認します。機密性の低いオリジンはオプトアウトするか、コントロールパネルの「同意と監査」セクションで「確認」に変更できます。すべてのケイパビリティ呼び出しは、墨消しされた追記専用のローカル監査ログに記録されます。引数、トークン、Cookie、レスポンスボディが保存されることは決してありません。オープンソースFSB は MIT ライセンスの下で完全にオープンソースです。プライバシーに関する主張はコードを 1 行ずつ監査して確認できます。ソースコードは GitHub で公開されています。本ポリシーの変更本ポリシーが更新された場合、ページ上部の「最終更新」日に反映されます。重大な変更は、プロジェクトの GitHub リリースノートにも記載されます。お問い合わせこのプライバシーポリシーや FSB のデータ取り扱いについて質問がある場合は、GitHub Issues で課題を作成してください。 src/app/pages/privacy/privacy-history-archive.component.html - 7,238 + 246,477 @@ -9567,7 +9587,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026 年 5 月 – 7 月v0.9.90 — サイト API 機能、Remote Dashboard と PhantomStream、支払い方法、音声入力、地域指標(アーカイブ全文) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 481,484 @@ -9579,7 +9599,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use への準拠表明FSBの匿名利用テレメトリーは、 full-selfbrowsing.com/statsで公開する集計利用統計の算出にのみ使用します。データを販売、第三者と共有、広告に使用、機械学習モデルの学習に使用することは一切ありません。この方針は Chrome Web Storeの Limited Use 要件を満たします。集計された公開メトリクスこのテレメトリーパイプラインから得た集計メトリクスを /statsで公開しています。表示するのは件数と合計だけで、インストール単位の行を公開することはありません。同意と監査の制御FSBの自動化方針はオプトアウト式です。拒否リスト外のオリジンは、現在「自動」で提供されるユーザー設定可能な全体既定値を継承し、オリジンごとの明示的なポリシーでは「オフ」「確認」「自動」を設定できます。監査ログはローカルに期限付きで保持され、コントロールパネルからエクスポートと消去を行えます。サービス拒否リストは引き続き強制ブロックです。拒否対象のオリジンは、全体既定値や保存済みのオリジン別ポリシーに関係なく有効化できません。機密性の高いオリジンでは「自動」で読み取りを実行できますが、書き込み前にはオリジン別の変更許可を再確認します。機密性の低いオリジンはオプトアウトするか、コントロールパネルの「同意と監査」セクションで「確認」に変更できます。すべてのケイパビリティ呼び出しは、墨消しされた追記専用のローカル監査ログに記録されます。引数、トークン、Cookie、レスポンスボディが保存されることは決してありません。オープンソースFSB は MIT ライセンスのもとで完全にオープンソースです。すべてのコードを監査して、これらのプライバシー上の主張を検証できます。ソースコードは GitHubを。本ポリシーの変更このポリシーを更新した場合、変更はページ上部の「最終更新日」に反映されます。重要な変更はプロジェクトの GitHub リリースノートにも記載します。お問い合わせこのプライバシーポリシーまたは FSBによるデータの取り扱いについて質問がある場合は、 GitHub Issuesを。 src/app/pages/privacy/privacy-history-archive.component.html - 7,198 + 485,676 @@ -9587,7 +9607,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026 年 3 月v9.0.2 — バックグラウンドエージェント、メモリーシステム、サーバー同期、匿名利用テレメトリー、法的方針(アーカイブ全文) src/app/pages/privacy/privacy-history-archive.component.html - 202,205 + 680,683 @@ -9599,7 +9619,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use への準拠表明FSBの匿名利用テレメトリーは、full-selfbrowsing.com/statsで公開する集計利用統計の算出にのみ使用します。データを販売、第三者と共有、広告に使用、機械学習モデルの学習に使用することは一切ありません。この方針はChrome Web StoreのLimited Use要件を満たします。集計された公開メトリクスこのテレメトリーパイプラインから得た集計メトリクスを/statsで公開しています。表示するのは件数と合計だけで、インストール単位の行を公開することはありません。法的方針と同意モデルFSBの自動化方針、監査ログの保持、オリジン単位の同意モデルは、明示的な製品コントロールとして提示されていました。FSBは、ユーザーが明示的に許可するまでオリジン上で何も行いません。自動読み取りアクセスが書き込みアクセスを意味することはなく、保守的なサービス拒否リストにより、機密カテゴリ(金融・行政サービス)での自動化を完全にブロックします。オリジン単位の同意は既定で無効です。自動アクセスと書き込み(変更)アクセスは個別の明示的オプトインであり、コントロールパネルの「同意と監査」セクションから管理します。すべてのケイパビリティ呼び出しは、墨消しされた追記専用のローカル監査ログに記録されます。引数、トークン、Cookie、レスポンスボディが保存されることは決してありません。サービス拒否リストによって機密オリジンは有効化できません。この制御はインターフェースだけでなく、ケイパビリティゲートでも適用されます。オープンソースFSBはMITライセンスのもとで完全にオープンソースです。すべてのコードを監査して、これらのプライバシー上の主張を検証できます。ソースコードはGitHubで公開されています。本ポリシーの変更このポリシーを更新した場合、変更はページ上部の「最終更新日」に反映されます。重要な変更はプロジェクトのGitHubリリースノートにも記載します。お問い合わせこのプライバシーポリシーまたはFSBによるデータの取り扱いについて質問がある場合は、GitHub Issuesで問題を報告してください。 src/app/pages/privacy/privacy-history-archive.component.html - 206,345 + 684,823 @@ -9607,7 +9627,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026 年 2 月v0.9 — 初版プライバシーポリシー(アーカイブ全文) src/app/pages/privacy/privacy-history-archive.component.html - 349,352 + 827,830 @@ -9615,7 +9635,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026年2月時点の初版プライバシーポリシーのアーカイブです。このスナップショットは元の静的ショーケースページからページ共通部分を除いて再現し、ポリシー本文をそのまま保存しています。データ収集FSBはブラウザ内だけで動作します。拡張機能がアクセスするのはDOM(Document Object Model)だけであり、自動化タスクを開始したときのアクティブなタブに限られます。現在のセッションを超えて閲覧履歴を収集・保存しませんDOMデータはローカルで解析され、自動化の各ステップ後に破棄されます訪問先ページから個人情報を抽出しませんデータ保存すべての設定とデータはローカルのChromeの拡張機能ストレージに保存されます。FSBはAES-GCMを使用して API キーなどの機密データを暗号化します。設定の保存先:chrome.storage.localAPI キーは保存前に次の方式で暗号化されます:AES-GCMセッションログはローカルに保存され、いつでも削除できます分析データ(タスク数、成功率)は端末上に残ります外部サービスFSBが外部の AI プロバイダーと通信するのは、設定して使用する場合だけです。プロバイダーの選択と送信内容はユーザーが管理できます。API 呼び出しは、選択したプロバイダー(xAI、OpenAI、Anthropic、またはGoogle)にのみ送信されます送信されるデータには、タスクの説明、DOM構造の概要、アクションのコンテキストが含まれますデータはFSBサーバーへ一切送信されません。そもそもサーバー自体が存在しません各プロバイダーは、API リクエストの取り扱いを定める独自のプライバシーポリシーを有しますトラッキングなしFSBには分析、テレメトリ、トラッキングの各サービスが一切含まれません。Cookie、フィンガープリント採取、明示的に設定した AI プロバイダー API 以外のサードパーティスクリプトもありません。API キーAPI キーはローカルでAES-GCMにより暗号化してから保存します。設定した AI プロバイダー以外へ送信することはなく、API リクエストの認証ヘッダーとしてのみ使用します。キーは保管時にChromeストレージ内で暗号化されます復号は API 呼び出し時にメモリ内でのみ行われますキーは記録、エクスポート、共有のいずれもされません自動パスワード入力(ベータ版)FSBには、ログイン認証情報を端末上で暗号化して保存する任意の認証情報マネージャーがあります。パスワードが AI モデルへ公開されることはなく、コンテンツスクリプトが AI を完全に介さずページへ直接入力します。認証情報は保管時にAES-GCMと 256 ビット鍵、およびPBKDF2による鍵導出を使用して暗号化されますAI がページを解析するとき、パスワード欄の値は[hidden]に置き換えられます。実際のパスワードが AI プロンプトに含まれることはありません自動入力は、コンテンツスクリプトが値をDOMへ直接挿入して行い、認証情報の処理に AI は一切関与しません認証情報の一覧にはユーザー名とドメインのみが表示されます。パスワードは自動入力に必要なときにのみ個別に復号されます認証情報はドメインごとに保存され、親ドメインへフォールバックします(例:accounts.google.comはgoogle.comを継承)プロンプトインジェクション対策Web ページには AI エージェントを乗っ取るための隠しテキストが含まれる場合があります。FSBは多層防御を実装し、AI がページ内容に埋め込まれた指示ではなく、ユーザーの指示だけに従うようにしています。すべてのページ内容は[PAGE_CONTENT]境界マーカーで囲まれ、AI にはこのマーカー内の指示に決して従わないよう指示されますサニタイズエンジンが既知のインジェクションパターン(例:「以前の指示を無視」、偽のシステムプロンプト、上書き試行)を、AI に渡る前にページ内容から取り除きますAI が生成したアクションは実行前に検証されます。危険な URL(javascript:、data:)やスクリプト挿入の試みはブロックされます実行できるのは厳格な許可リストに登録された 30 種類以上の既知ツールだけです。AI が任意のアクションを作成したり呼び出したりすることはできませんコンテンツサイズには上限があり(値あたり 500 文字、プロンプト全体 15K)、ペイロードの送り込みを制限しますWeb サイトが埋め込む不可視の Unicode 制御文字は処理前に取り除かれますオープンソースFSBはMITライセンスのもとで完全にオープンソースです。すべてのコードを監査して、これらのプライバシー上の主張を検証できます。ソースコードはGitHubで公開されています。GitHub でソースコードを見る概要FSBはデータをローカルで処理し、機密情報を暗号化し、パスワードを AI モデルに公開せず、プロンプトインジェクション攻撃を防ぎます。また、選択した AI プロバイダーとのみ通信し、トラッキングを一切含まず、オープンソースソフトウェアとして完全に監査できます。 src/app/pages/privacy/privacy-history-archive.component.html - 353,420 + 831,898 @@ -9635,8 +9655,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Last updated: September 2026 - 最終更新: 2026 年 9 月 + Last updated: September 29, 2026 + 最終更新: 2026 年 9 月 29 日 src/app/pages/privacy/privacy-page.component.html 6,9 @@ -10563,8 +10583,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. - FSB v0.9.69 では、プロジェクトが集計された導入数を公開できるよう、オプトアウト可能な匿名利用テレメトリパイプラインを導入しました(/stats を参照)。閲覧中のページに触れることは一切ありません。唯一の位置情報シグナルは、サーバーが取り込み時にリクエストの IP から導出する大まかな国/州ラベルであり(ページ内容からではありません)、集計形式でのみ公開されます。詳しくは下記の 地域(州レベル)指標 を参照してください。テレメトリは既定で有効ですが、1 つのトグルで無効化でき、インストールごとのデータはリクエストにより消去できます。 + FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse place label — city, state or province, and country — that the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (city-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. + FSB v0.9.69 では、プロジェクトが集計された導入数を公開できるよう、オプトアウト可能な匿名利用テレメトリパイプラインを導入しました(/stats を参照)。閲覧中のページに触れることは一切ありません。唯一の位置情報シグナルは、都市、州または県、国からなる大まかな場所ラベルです。これはサーバーが取り込み時にリクエストの IP から導出するもので(ページ内容からではありません)、集計形式でのみ公開されます。詳しくは下記の 地域(都市レベル)指標 を参照してください。テレメトリは既定で有効ですが、1 つのトグルで無効化でき、インストールごとのデータはリクエストにより消去できます。 src/app/pages/privacy/privacy-page.component.html 197,199 @@ -10651,8 +10671,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. - 平文の IP アドレス。サーバーはリクエストの IP を、ちょうど 3 つの目的(レート制限のための日次ローテーションソルトによるハッシュ、大まかな国/州ラベルの導出(下記参照)、およびアドレスが IPv4 と IPv6 のどちらであるかの記録)のために一時的かつインラインで使用し、その後直ちに破棄します。平文の IP が保存またはログ記録されることは決してありません。 + Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse city, state, and country label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. + 平文の IP アドレス。サーバーはリクエストの IP を、ちょうど 3 つの目的(レート制限のための日次ローテーションソルトによるハッシュ、大まかな都市/州/国ラベルの導出(下記参照)、およびアドレスが IPv4 と IPv6 のどちらであるかの記録)のために一時的かつインラインで使用し、その後直ちに破棄します。平文の IP が保存またはログ記録されることは決してありません。 src/app/pages/privacy/privacy-page.component.html 213,214 @@ -10675,43 +10695,51 @@ https://full-selfbrowsing.com/api/telemetry/forget - Region (state-level) metric - 地域(州レベル)指標 + Region (city-level) metric + 地域(都市レベル)指標 src/app/pages/privacy/privacy-page.component.html 218,220 - The server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. - サーバーは取り込み時に、自己ホストの IP DB-IP-to-City Lite データセットと独自のルックアップを用いて、リクエストの IP から大まかな国および米国の州(サブディビジョン)ラベルを導出します。これはライブのサードパーティ地理位置情報サービスではなく、MaxMind でもありません。平文の IP はインラインで処理されて破棄され、導出されたラベルとアドレスファミリー(IPv4 または IPv6)のみが保持されます。 + The server derives a coarse place label — country, state or province, and city — from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. + サーバーは取り込み時にリクエストの IP から、自己ホストの DB-IP IP-to-City Lite データセットと独自のルックアップを用いて、大まかな場所ラベル(国、州または県、都市)を導出します。これはライブのサードパーティ地理位置情報サービスではなく、MaxMind でもありません。平文の IP はインラインで処理されて破棄され、導出されたラベルとアドレスファミリー(IPv4 または IPv6)のみが保持されます。 src/app/pages/privacy/privacy-page.component.html 220,221 - Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs. - 地域は集計形式でのみ、k≥5 の匿名性しきい値の背後で公開されます。固有インストール数が 5 未満の州はすべて単一の「Other」バケットにまとめられます(その合計数自体が 5 未満の場合は完全に抑制されます)。公開される州ラベルが 5 未満のインストールを表すことは決してありません。 + Region is published only in aggregate, behind a k≥5 anonymity floor applied level by level: an install is counted under its city when at least 5 distinct installs share that city, otherwise under its state or province, then its country, by the same rule. Anything still below 5 collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). Each install is counted in exactly one bucket, and no published city, state, or country label ever represents fewer than 5 installs. + 地域は集計形式でのみ、階層ごとに適用される k≥5 の匿名性しきい値の背後で公開されます。インストールは、同じ都市を共有する固有インストールが 5 以上ある場合はその都市で数えられ、そうでない場合は同じ規則に従ってまず州または県、次に国で数えられます。それでも 5 未満のものはすべて単一の「Other」バケットにまとめられます(その合計数自体が 5 未満の場合は完全に抑制されます)。各インストールはちょうど 1 つのバケットで数えられ、公開される都市・州・国のラベルが 5 未満のインストールを表すことは決してありません。 src/app/pages/privacy/privacy-page.component.html 221 - - The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). - 大まかなラベルは、生イベント(7 日間の保持期間で削除)と、インストールの日次ロールアップに保存されます。日次ロールアップは、/stats が各インストールを最新の地域で 1 回だけ数えられるよう 365 日間保持されます。このラベルが IP アドレス、都市、座標であることはありません。消去をリクエストすると、インストールごとの他のデータとともに削除され、上記の k≥5 しきい値の背後でのみ公開されます。地理位置情報データは DB-IP(https://db-ip.com)によります。 + + The /stats globe places each published label at the approximate center of that city, state, or country, taken from the same dataset — never at the location of any install. + /stats の地球儀は、公開された各ラベルを、同じデータセットから取得したその都市・州・国のおおよその中心に配置します。個々のインストールの位置に配置することは決してありません。 src/app/pages/privacy/privacy-page.component.html 222,223 + + The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, street address, or coordinates — a city name is the finest detail kept; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). + 大まかなラベルは、生イベント(7 日間の保持期間で削除)と、インストールの日次ロールアップに保存されます。日次ロールアップは、/stats が各インストールを最新の地域で 1 回だけ数えられるよう 365 日間保持されます。このラベルが IP アドレス、番地を含む住所、座標であることはなく、保持される最も細かい情報は都市名です。消去をリクエストすると、インストールごとの他のデータとともに削除され、上記の k≥5 しきい値の背後でのみ公開されます。地理位置情報データは DB-IP(https://db-ip.com)によります。 + + src/app/pages/privacy/privacy-page.component.html + 223,224 + + Retention 保持期間 src/app/pages/privacy/privacy-page.component.html - 225,226 + 226,227 @@ -10719,7 +10747,7 @@ https://full-selfbrowsing.com/api/telemetry/forget生イベントは 7 日間保持されます。日次ロールアップ (インストールごと、日ごとに 1 行。大まかな地域ラベルを含む) は 365 日間保持されます。グローバル集計 (日ごとに 1 行、インストール次元なし) は /stats の履歴チャートが安定するよう無期限に保持されます。 src/app/pages/privacy/privacy-page.component.html - 226,228 + 227,229 @@ -10727,7 +10755,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetオプトアウトの方法 src/app/pages/privacy/privacy-page.component.html - 228,229 + 229,230 @@ -10735,7 +10763,7 @@ https://full-selfbrowsing.com/api/telemetry/forget「FSB コントロールパネル」を開き、「詳細設定」までスクロールして、匿名利用データを送信 をオフにしてください。変更は直ちに反映され、それ以降このインストール環境からイベントは送信されません。 src/app/pages/privacy/privacy-page.component.html - 229,231 + 230,232 @@ -10743,7 +10771,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetデータを消去する方法 src/app/pages/privacy/privacy-page.component.html - 231,232 + 232,233 @@ -10751,7 +10779,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetインストールに関連するすべてのテレメトリ行の消去を依頼するには(GDPR 第 17 条)、fsbInstallUuid を Chrome DevTools → アプリケーション → ストレージ → 拡張機能ストレージ で確認し、次の HTTP リクエストを 1 回送信します: src/app/pages/privacy/privacy-page.component.html - 232,233 + 233,234 @@ -10763,7 +10791,7 @@ https://full-selfbrowsing.com/api/telemetry/forget src/app/pages/privacy/privacy-page.component.html - 233,237 + 234,238 @@ -10771,7 +10799,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use への準拠表明 src/app/pages/privacy/privacy-page.component.html - 237,238 + 238,239 @@ -10779,7 +10807,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB の匿名利用テレメトリーは、full-selfbrowsing.com/stats で公開される集計利用統計の算出にのみ使用されます。データは決して販売されず、第三者と共有されず、広告のために使用されず、機械学習モデルの学習にも使用されません。この方針は Chrome Web Store の Limited Use 要件を満たします。 src/app/pages/privacy/privacy-page.component.html - 238,240 + 239,241 @@ -10787,7 +10815,7 @@ https://full-selfbrowsing.com/api/telemetry/forget集計された公開メトリクス src/app/pages/privacy/privacy-page.component.html - 240,241 + 241,242 @@ -10795,7 +10823,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetこのテレメトリーパイプラインから導出された集計メトリクスを /stats で公開しています。表示されるのは件数と合計のみで、インストールごとの行は決して公開されません。 src/app/pages/privacy/privacy-page.component.html - 241,243 + 242,244 @@ -10803,7 +10831,7 @@ https://full-selfbrowsing.com/api/telemetry/forget同意と監査の制御 src/app/pages/privacy/privacy-page.component.html - 244,245 + 245,246 @@ -10811,7 +10839,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB の自動化方針はオプトアウト式です。拒否リスト外のオリジンは、現在「自動」で提供されるユーザー設定可能な全体既定値を継承し、オリジンごとの明示的なポリシーでは「オフ」「確認」「自動」を設定できます。監査ログはローカルに期限付きで保持され、コントロールパネルからエクスポートと消去を行えます。 src/app/pages/privacy/privacy-page.component.html - 245,247 + 246,248 @@ -10819,7 +10847,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetサービス拒否リストは引き続き強制ブロックです。拒否対象のオリジンは、全体既定値や保存済みのオリジン別ポリシーに関係なく有効化できません。 src/app/pages/privacy/privacy-page.component.html - 247,248 + 248,249 @@ -10827,7 +10855,7 @@ https://full-selfbrowsing.com/api/telemetry/forget機密性の高いオリジンでは「自動」で読み取りを実行できますが、書き込み前にはオリジン別の変更許可を再確認します。機密性の低いオリジンはオプトアウトするか、コントロールパネルの「同意と監査」セクションで「確認」に変更できます。 src/app/pages/privacy/privacy-page.component.html - 248,249 + 249,250 @@ -10835,7 +10863,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetすべてのケイパビリティ呼び出しは、墨消しされた追記専用のローカル監査ログに記録されます。引数、トークン、Cookie、レスポンスボディが保存されることは決してありません。 src/app/pages/privacy/privacy-page.component.html - 249,252 + 250,253 @@ -10843,7 +10871,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetオープンソース src/app/pages/privacy/privacy-page.component.html - 253,254 + 254,255 @@ -10851,7 +10879,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB は MIT ライセンスの下で完全にオープンソースです。プライバシーに関する主張はコードを 1 行ずつ監査して確認できます。ソースコードは GitHub で公開されています。 src/app/pages/privacy/privacy-page.component.html - 254,256 + 255,257 @@ -10859,7 +10887,7 @@ https://full-selfbrowsing.com/api/telemetry/forget本ポリシーの変更 src/app/pages/privacy/privacy-page.component.html - 257,258 + 258,259 @@ -10867,7 +10895,7 @@ https://full-selfbrowsing.com/api/telemetry/forget本ポリシーが更新された場合、ページ上部の「最終更新」日に反映されます。重大な変更は、プロジェクトの GitHub リリースノートにも記載されます。 src/app/pages/privacy/privacy-page.component.html - 258,260 + 259,261 @@ -10875,7 +10903,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetお問い合わせ src/app/pages/privacy/privacy-page.component.html - 261,262 + 262,263 @@ -10883,7 +10911,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetこのプライバシーポリシーや FSB のデータ取り扱いについて質問がある場合は、GitHub Issues で課題を作成してください。 src/app/pages/privacy/privacy-page.component.html - 262,264 + 263,265 @@ -10891,7 +10919,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetポリシー履歴 src/app/pages/privacy/privacy-page.component.html - 265,266 + 266,267 @@ -10899,7 +10927,7 @@ https://full-selfbrowsing.com/api/telemetry/forget以下の各項目は、表示された日付時点のプライバシーポリシーを完全に保存したものです。過去のどの時点で何を約束していたか監査できるよう、旧版も保管しています。 src/app/pages/privacy/privacy-page.component.html - 266,268 + 267,269 @@ -11623,7 +11651,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetブラウザーを操作中 src/app/pages/release-notes/release-notes-page.component.html - 176,180 + 176,179 @@ -11775,7 +11803,7 @@ https://full-selfbrowsing.com/api/telemetry/forget入力不可 · このタブは別の自動化が所有しています src/app/pages/release-notes/release-notes-page.component.html - 226,231 + 226,230 @@ -12351,7 +12379,7 @@ https://full-selfbrowsing.com/api/telemetry/forget要素 src/app/pages/release-notes/release-notes-page.component.html - 408,412 + 408,411 @@ -12511,7 +12539,7 @@ https://full-selfbrowsing.com/api/telemetry/forget560 件が保護対象 src/app/pages/release-notes/release-notes-page.component.html - 469,473 + 469,472 @@ -12671,7 +12699,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetnode 24 src/app/pages/release-notes/release-notes-page.component.html - 529,533 + 529,532 @@ -17999,7 +18027,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB - リリースノート src/app/pages/release-notes/release-notes-page.component.ts - 88 + 86 @@ -18007,7 +18035,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB のすべてのリリースを図解で紹介します。委任されたエージェントランタイム、ツールバーの動作アイコン、マルチエージェントのタブ所有権、MCP ブリッジ、ケイパビリティカタログ、そして最初の Chrome 拡張機能の試作まで。 src/app/pages/release-notes/release-notes-page.component.ts - 89 + 87 @@ -18179,11 +18207,11 @@ https://full-selfbrowsing.com/api/telemetry/forget - Where installs were last seen, by coarse region, over the past 365 days. - 過去 365 日間にインストールが最後に確認された場所を、大まかな地域別に示します。 + Where installs were last seen over the past 365 days, by city, state, or country. Place data by DB-IP. + 過去 365 日間にインストールが最後に確認された場所を、都市・州・国別に示します。場所データ提供: DB-IP。 src/app/pages/stats/stats-page.component.html - 104,106 + 104,105 @@ -18239,7 +18267,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetネットワークまたは解析エラー。 src/app/pages/stats/stats-page.component.ts - 132 + 133 @@ -18247,7 +18275,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetスター src/app/pages/stats/stats-page.component.ts - 139 + 140 @@ -18255,7 +18283,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetコミット src/app/pages/stats/stats-page.component.ts - 140 + 141 @@ -18263,7 +18291,7 @@ https://full-selfbrowsing.com/api/telemetry/forget現在アクティブ src/app/pages/stats/stats-page.component.ts - 141 + 142 @@ -18271,7 +18299,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetトークン src/app/pages/stats/stats-page.component.ts - 142 + 143 @@ -18279,27 +18307,7 @@ https://full-selfbrowsing.com/api/telemetry/forget人気 src/app/pages/stats/stats-page.component.ts - 143 - - - - Could not load chart library. - グラフライブラリを読み込めませんでした。 - - src/app/pages/stats/stats-page.component.ts - 183 - - - - Could not render the selected chart. - 選択したグラフを表示できませんでした。 - - src/app/pages/stats/stats-page.component.ts - 190 - - - src/app/pages/stats/stats-page.component.ts - 862 + 144 @@ -18307,7 +18315,7 @@ https://full-selfbrowsing.com/api/telemetry/forget統計を準備しています。まもなく再試行します。 src/app/pages/stats/stats-page.component.ts - 232 + 233 @@ -18315,7 +18323,7 @@ https://full-selfbrowsing.com/api/telemetry/forget統計の応答に鮮度メタデータが含まれていません。 src/app/pages/stats/stats-page.component.ts - 235 + 236 @@ -18323,7 +18331,7 @@ https://full-selfbrowsing.com/api/telemetry/forget利用可能な最新のスナップショットは 24 時間以上前のものです。 src/app/pages/stats/stats-page.component.ts - 238 + 239 @@ -18331,7 +18339,7 @@ https://full-selfbrowsing.com/api/telemetry/forget統計の応答が不正な形式でした。 src/app/pages/stats/stats-page.component.ts - 241 + 242 @@ -18339,7 +18347,7 @@ https://full-selfbrowsing.com/api/telemetry/forget統計はブラウザでのみ利用できます。 src/app/pages/stats/stats-page.component.ts - 244 + 245 @@ -18347,7 +18355,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetリポジトリの累積スター数の推移 src/app/pages/stats/stats-page.component.ts - 252 + 253 @@ -18355,7 +18363,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetリポジトリの累積コミット数の推移 src/app/pages/stats/stats-page.component.ts - 254 + 255 @@ -18363,7 +18371,7 @@ https://full-selfbrowsing.com/api/telemetry/forget過去30日間のFSBトークン使用量 src/app/pages/stats/stats-page.component.ts - 256 + 257 @@ -18371,7 +18379,7 @@ https://full-selfbrowsing.com/api/telemetry/forget追跡対象MCPクライアントの割合 src/app/pages/stats/stats-page.component.ts - 258 + 259 @@ -18379,7 +18387,7 @@ https://full-selfbrowsing.com/api/telemetry/forget不明 src/app/pages/stats/stats-page.component.ts - 295 + 296 @@ -18387,15 +18395,15 @@ https://full-selfbrowsing.com/api/telemetry/forgetその他 src/app/pages/stats/stats-page.component.ts - 296 + 297 - Globe showing where FSB installs were last seen over the past 365 days, by coarse region - 過去 365 日間に FSB のインストールが最後に確認された場所を大まかな地域別に示す地球儀 + Globe showing where FSB installs were last seen over the past 365 days, by city, state, or country + 過去 365 日間に FSB のインストールが最後に確認された場所を都市・州・国別に示す地球儀 src/app/pages/stats/stats-page.component.ts - 308 + 309 @@ -18403,7 +18411,7 @@ https://full-selfbrowsing.com/api/telemetry/forget総スター数 src/app/pages/stats/stats-page.component.ts - 317 + 318 @@ -18411,7 +18419,7 @@ https://full-selfbrowsing.com/api/telemetry/forget過去 7 日間 src/app/pages/stats/stats-page.component.ts - 318 + 319 @@ -18419,7 +18427,7 @@ https://full-selfbrowsing.com/api/telemetry/forget総コミット数 src/app/pages/stats/stats-page.component.ts - 325 + 326 @@ -18427,7 +18435,7 @@ https://full-selfbrowsing.com/api/telemetry/forget過去 30 日間 src/app/pages/stats/stats-page.component.ts - 329 + 330 @@ -18435,7 +18443,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetアクティブエージェント src/app/pages/stats/stats-page.component.ts - 343 + 344 @@ -18443,7 +18451,7 @@ https://full-selfbrowsing.com/api/telemetry/forget平均/報告ユーザー src/app/pages/stats/stats-page.component.ts - 347 + 348 @@ -18451,7 +18459,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetトークン src/app/pages/stats/stats-page.component.ts - 357 + 358 @@ -18459,7 +18467,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetトークン(24時間) src/app/pages/stats/stats-page.component.ts - 358 + 359 @@ -18467,7 +18475,7 @@ https://full-selfbrowsing.com/api/telemetry/forget追跡対象クライアント src/app/pages/stats/stats-page.component.ts - 364 + 365 @@ -18475,7 +18483,7 @@ https://full-selfbrowsing.com/api/telemetry/forget上位: src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18483,7 +18491,7 @@ https://full-selfbrowsing.com/api/telemetry/forget最多クライアント src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18491,7 +18499,7 @@ https://full-selfbrowsing.com/api/telemetry/forget統計ビューを選択します。現在のビュー: src/app/pages/stats/stats-page.component.ts - 402 + 403 @@ -18499,7 +18507,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB · 統計 src/app/pages/stats/stats-page.component.ts - 478 + 484 @@ -18507,7 +18515,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSBの導入状況、利用状況、リポジトリシグナルをリアルタイムで集計。 src/app/pages/stats/stats-page.component.ts - 486 + 492 @@ -18515,7 +18523,7 @@ https://full-selfbrowsing.com/api/telemetry/forget累計スター数 src/app/pages/stats/stats-page.component.ts - 974 + 979 @@ -18523,7 +18531,7 @@ https://full-selfbrowsing.com/api/telemetry/forget累計コミット数 src/app/pages/stats/stats-page.component.ts - 995 + 1000 @@ -18531,7 +18539,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetトークン(過去30日間) src/app/pages/stats/stats-page.component.ts - 1023 + 1028 @@ -18539,7 +18547,23 @@ https://full-selfbrowsing.com/api/telemetry/forget人気の MCP クライアント src/app/pages/stats/stats-page.component.ts - 1046 + 1051 + + + + Could not load chart library. + グラフライブラリを読み込めませんでした。 + + src/app/pages/stats/stats-page.component.ts + 1105 + + + + Could not render the selected chart. + 選択したグラフを表示できませんでした。 + + src/app/pages/stats/stats-page.component.ts + 1106 diff --git a/showcase/angular/src/locale/messages.ko.xlf b/showcase/angular/src/locale/messages.ko.xlf index b19d09b6..d4fb3fb4 100644 --- a/showcase/angular/src/locale/messages.ko.xlf +++ b/showcase/angular/src/locale/messages.ko.xlf @@ -5515,7 +5515,7 @@ 마지막 스냅숏: src/app/pages/dashboard/dashboard-page.component.ts - 3994 + 4014 @@ -5523,7 +5523,7 @@ 상태: src/app/pages/dashboard/dashboard-page.component.ts - 3998 + 4018 @@ -5531,7 +5531,7 @@ 사유: src/app/pages/dashboard/dashboard-page.component.ts - 4002 + 4022 @@ -5539,7 +5539,7 @@ 복구 중 경과 초 src/app/pages/dashboard/dashboard-page.component.ts - 4006 + 4026 @@ -5547,7 +5547,7 @@ 마지막 프레임: 초 전 src/app/pages/dashboard/dashboard-page.component.ts - 4012 + 4032 @@ -5555,7 +5555,7 @@ 변경: src/app/pages/dashboard/dashboard-page.component.ts - 4013 + 4033 @@ -5563,7 +5563,7 @@ 적용 실패: src/app/pages/dashboard/dashboard-page.component.ts - 4014 + 4034 @@ -5571,7 +5571,7 @@ 오래됨: src/app/pages/dashboard/dashboard-page.component.ts - 4015 + 4035 @@ -5579,7 +5579,7 @@ 스트림 데이터 없음 src/app/pages/dashboard/dashboard-page.component.ts - 4016 + 4036 @@ -5587,7 +5587,7 @@ 요청이 실패했습니다. 상태 코드 src/app/pages/dashboard/dashboard-page.component.ts - 4319 + 4339 @@ -5595,7 +5595,7 @@ 약 분 남음 src/app/pages/dashboard/dashboard-page.component.ts - 4474 + 4494 @@ -5603,7 +5603,7 @@ 약 초 남음 src/app/pages/dashboard/dashboard-page.component.ts - 4475 + 4495 @@ -5611,7 +5611,7 @@ 실행 시간 src/app/pages/dashboard/dashboard-page.component.ts - 4549 + 4569 @@ -5619,7 +5619,7 @@ 사용자가 중지함 — 진행 중이던 작업: src/app/pages/dashboard/dashboard-page.component.ts - 4554 + 4574 @@ -9542,12 +9542,32 @@ 307 + + September 2026v0.9.91 — 365-day region retention, Region (state-level) metric (full archived text) + 2026년 9월v0.9.91 — 지역 정보 365일 보관, 지역(주 단위) 지표 (아카이브 전문) + + src/app/pages/privacy/privacy-history-archive.component.html + 3,6 + + + + Archived copy of the privacy policy as it stood from September 28, 2026, before the September 29, 2026 update.TLDR: FSB runs inside your browser, and no browsing data is collected by FSB servers. AI calls go directly from your browser to the provider you choose. API keys and credentials are encrypted locally, and memory data stays on your device. If you install the optional local agent service, FSB can also be driven by an MCP client running on your own machine over a loopback connection, and by coding-agent CLIs you have already installed. The only data that ever leaves your machine for FSB is opt-out anonymous usage telemetry that powers the public /stats page. If you pair Remote Dashboard, transient live-preview frames can pass through the relay during the session, but they are not stored. Everything is open source and auditable.Data CollectionFSB operates entirely within your browser. When you initiate an automation task, the extension may inspect the active tab's DOM (Document Object Model) and, when you invoke a capability, make same-origin site API requests from your browser session.No browsing history is collected or stored beyond the current sessionDOM data and site API results are analyzed locally and discarded after each automation step unless you explicitly save them in memoryNo personal information is harvested from pages you visitSite API capabilitiesFSB's capability layer can call a site's own first-party API from the page context, using your already-authenticated browser session. These requests run locally in Chrome; first-party cookies or site auth may be attached by the browser for that target site, but FSB does not return, store, log, or send cookies, tokens, CSRF values, request bodies, or response bodies to FSB servers.Capability invocations follow FSB's consent controls and are recorded only in the redacted local audit log: origin, capability slug, method, side-effect class, consent decision, and outcome. The audit log never stores invocation arguments, request bodies, response bodies, cookies, tokens, CSRF values, or site response payloads.Chrome permissions FSB requestsTo run web automation, FSB declares the following permissions in its Chrome manifest. Each is used only for the documented purpose; nothing is sent off-device on the strength of any of them.DOM and tabs, activeTab, scripting, tabs, windows, sidePanel, and host permission <all_urls>: read and write the active tab, inject the automation content script, list and switch tabs, and render the side panelAdvanced automation, debugger: attach the Chrome DevTools Protocol for coordinate-based clicks, drag, and key-hold actions that the regular DOM API cannot perform. webNavigation: observe navigation start/finish events so automation waits for the right momentLocal storage, storage, unlimitedStorage: store your settings, credentials, payment methods, and memory in chrome.storage.local on your device. Unlimited storage lifts the default 10 MB quota so memory and session logs can grow without hitting a wallUX helpers, clipboardWrite: write copy-to-clipboard results from automation. alarms: schedule background housekeeping. offscreen: host the speech-to-text recorder in a hidden document because service workers cannot capture audio directlyLocal service, nativeMessaging: start the optional local agent service described under Local Agent Service and Native Messaging below. Nothing about the pages you visit crosses that channel. system.memory: read the total installed RAM figure only, so FSB can suggest a sensible limit on how many agents run at once. The available-memory and per-processor breakdowns are never read, and the figure never leaves the extensionMicrophone access for speech-to-text is not declared in the manifest. Chrome shows its own permission prompt the first time you use the mic button.Data StorageAll settings and data are stored locally in Chrome's extension storage, with sensitive values encrypted at rest as described in the sections below.Configuration is stored in chrome.storage.localAPI keys, credentials, and payment methods are encrypted before storage, each detailed in its own section belowSession logs are stored locally and can be cleared at any timeAnalytics data (task counts, success rates) stays on your deviceExternal ServicesFSB communicates with external AI providers only when you configure and use a hosted provider. If you use LM Studio, AI requests stay on your machine through its local OpenAI-compatible server. The choice of provider and what data is sent is under your control.Hosted API calls are made only to the provider you select (xAI, OpenAI, Anthropic, Google, or OpenRouter)LM Studio uses a local OpenAI-compatible server on your device and does not require an API keySent data includes: task description, DOM structure summary, and action contextIf you pair Remote Dashboard or use legacy Background Agents sync, an optional relay server handles WebSocket messages for that session. Live-preview frames may pass through the relay transiently, but none of the following is ever persisted on an FSB server: page content, DOM data, browsing history, screenshots, AI prompts, AI responses, cookies, tokens, or site API payloads. This is opt-in only, and the rest of this policy refers back to this list rather than restating itEach provider has their own privacy policy governing how they handle API requestsLocal Agent Service and Native MessagingFSB can be driven by an MCP client running on your own machine. That requires a small local agent service which you install yourself, and the nativeMessaging permission exists solely so the extension can start it. Chrome cannot launch a local program directly, so this is the only supported mechanism.The messaging host io.github.fullselfbrowsing.fsb_native_host is registered only by an explicit action you take, by running fsb-mcp-server install --native-host. The extension cannot install it, and its registration names one permitted extension originThe exchange is a closed schema. FSB sends a version number, an action of either wake or bootstrap, and a locally generated correlation identifier; it receives that identifier back with an outcome and a reason drawn from a fixed list. Any additional field is rejected outright and messages are capped at 4 KB. No URL, tab, page content, or DOM data exists anywhere in this schemaA successful start returns a pairing code. This is a loopback authentication secret generated on your device and stored with owner-only permissions, rotated every time the service starts. It carries no information about you or your browsingThe service listens on your machine only. The extension bridge refuses to bind any address that is not loopback, and the MCP endpoint binds 127.0.0.1 by default and whenever FSB starts itUnder its own directory the service keeps only operational records: a diagnostics log of delegated-run outcomes restricted to a fixed field list, and a process-supervision journal. Task text, environment values, binary paths, and error text never reach either of them, and entries carrying credential-shaped values are discarded rather than writtenIf the host is not installed, the feature simply does not start. Nothing is transmitted, and the rest of the extension continues to work normallyDelegated CLI AgentsFSB can hand a task to a coding-agent command-line tool you have already installed, currently Claude Code or Grok Build. FSB starts that program on your own machine; your task is never proxied through FSB servers.Your task text reaches the tool over its standard input and nowhere else. FSB checks this before every run: if the task appears in the command line, the working directory, the environment, or any file path, the run is aborted rather than started. Tasks are capped at 64 KBWhile the run is in progress the tool calls back into FSB over the loopback connection to drive your browser. Whatever it reads that way — page text, DOM snapshots, spreadsheet values, screenshots — is returned to that tool, and therefore reaches its vendor: Anthropic for Claude Code, xAI for Grok BuildThe run uses the account already signed in to that tool. FSB does not hold, read, or transmit your Anthropic or xAI credential, and any usage or charges follow that vendor's own planGrok Build runs inside a profile directory FSB keeps separate from your own. The sign-in credential stored there is written by the Grok tool itself during its own sign-in flowThe agent instructions FSB supplies state that passwords, CVV values, payment-card data, and saved credentials must never enter prompts, narration, logs, or tool argumentsReading and Filling Google SheetsFSB can read and fill a range in Google Sheets by driving the spreadsheet interface in your own already-authenticated browser session.Reading a range returns those cell values to the model you selected, exactly like any other page content FSB reads on your behalf. Only the range you asked for is read; there is no whole-spreadsheet exportSpreadsheet content is stripped out before anything is written to FSB's local session history. Only shape counts survive — how many rows, columns, and values were involved. The spreadsheet address, sheet name, cell references, and every cell value are discardedIf that filtering cannot be applied to a record for any reason, the record is dropped rather than storedRemote Dashboard and PhantomStream Live PreviewWhen you pair Remote Dashboard, FSB can show a live preview of the active browser tab using PhantomStream. The preview streams structured DOM data instead of pixels: an initial snapshot, MutationObserver diffs, scroll position, automation overlays, dialog state, media playback state, and remote-control messages over WebSocket.The relay forwards those live-preview frames only to the paired dashboard session, and nothing from the preview stream is persisted on FSB servers — see the list under External Services aboveFSB enables PhantomStream input masking (maskInputs: true), so passwords and form-control values are masked before they leave the captured pageThe dashboard viewer renders mirrored content in a scriptless sandbox and sanitizes mirrored DOM and CSS before displayImages, video, and audio are mirrored by reference in current dashboard mode. Media bytes do not cross the relay; the viewer may fetch allowed public HTTPS asset or media URLs directly, while private, internal, non-HTTPS, or otherwise blocked origins are replaced with placeholders by PhantomStream's fail-closed fetch policyNo Third-Party TrackingFSB does not include any third-party analytics, ad trackers, or cross-site fingerprinting. There are no cookies and no third-party scripts beyond the AI provider APIs you explicitly configure. The one piece of first-party data FSB sends home is the opt-out Anonymous Usage Telemetry described below, used solely to power the public /stats dashboard.API KeysYour API keys are encrypted locally using AES-GCM before being stored. They are never transmitted anywhere except to the AI provider you configured, and only as authentication headers in API requests.Keys are encrypted at rest in Chrome storageDecryption only happens in-memory when making API callsKeys are never logged, exported, or sharedAuto-PasswordsFSB includes an optional credential manager that stores login credentials encrypted on your device. Passwords are never exposed to AI models. They are filled directly into pages by the content script, bypassing the AI entirely.Credentials are encrypted at rest using AES-GCM with 256-bit keys and PBKDF2 key derivationWhen the AI analyzes a page, password field values are replaced with [hidden]. The actual password is never included in any AI promptAuto-fill is performed by the content script injecting values directly into the DOM, with no AI involvement in the credential flowThe credential list view only shows usernames and domains. Passwords are decrypted individually and only when needed for auto-fillCredentials are stored per-domain with parent domain fallback (e.g., accounts.google.com inherits from google.com)Payment MethodsFSB includes an optional payment-method vault that stores card details on your device for checkout auto-fill. Cards are treated with the same encryption and AI isolation as login credentials, and the full card number is never sent to any AI model.Card details (number, expiry, cardholder, and zip) are encrypted at rest using AES-GCM with the same vault-derived key used for credentialsWhen the AI analyzes a checkout page, any detected card-number field values are replaced with [hidden] before the prompt is built. Card numbers, CVV, and expiry are never included in any AI promptAuto-fill happens via the content script writing directly into the page's DOM fields, bypassing the AI entirelyThe list view shows only a card nickname and last-4 digits. Full numbers are decrypted in memory only at the moment of fillAn MCP client can request a payment fill via use_payment_method, but the user is shown an in-extension confirmation prompt before any card data is written into the pageCVV is never persisted unless you opt in per-card, and even then it is encrypted alongside the rest of the recordSpeech-to-TextFSB includes an optional microphone input for the prompt box. The default provider runs entirely in your browser; an optional OpenAI Whisper fallback can be enabled in settings if you want higher accuracy.Default provider: the browser's native SpeechRecognition API. Audio is processed by Chrome and never leaves your device through FSBOptional Whisper provider: when sttProvider is set to whisper and an OpenAI key is configured, recorded audio chunks are uploaded directly from your browser to OpenAI's transcription endpoint. FSB never sees or stores the audioThe microphone is only active while you are holding or have toggled the mic button. Chrome prompts for permission the first time you use it; FSB does not request microphone access in the extension manifestTranscripts are inserted into the prompt textarea only and are never logged, persisted, or transmitted outside the active AI request you choose to sendDisable speech entirely by leaving the mic button untouched, or by clearing the optional Whisper provider in Chrome extension storagePrompt Injection PreventionWeb pages can contain hidden text designed to hijack AI agents. FSB implements multi-layered defenses to ensure the AI only follows your instructions, never instructions embedded in page content.All page content is wrapped in [PAGE_CONTENT] boundary markers, and the AI is instructed to never follow instructions found within these markersA sanitization engine strips known injection patterns (e.g., "ignore previous instructions", fake system prompts, override attempts) from all page content before it reaches the AIAI-generated actions are validated before execution. Dangerous URLs (javascript:, data:) and script injection attempts are blockedOnly a strict, fixed allowlist of known tools can be executed. The AI cannot invent or call arbitrary actionsContent size is capped (500 chars per value, 15K total prompt cap) to limit payload deliveryInvisible Unicode control characters that websites embed are stripped before processingBackground Agents and Server SyncDeprecated in v0.9.45rc1. FSB's built-in Background Agents have been superseded by OpenClaw and Claude Routines, with remote control now handled by the Sync tab. The disclosures below are retained for users still running v0.9.44 or earlier; on current builds the relay server is only contacted when you pair a Sync session.If you opt into legacy Background Agents server sync or pair Remote Dashboard, a relay server facilitates communication between your extension and the dashboard.The server stores: agent definitions (name, schedule, target URL), run metrics (token count, cost, duration, success/fail status), and session pairing tokensBeyond that run metadata the server persists nothing derived from the pages you visit — see the list under External Services aboveAuthentication uses hash keys (generated locally) and session tokens that expire after 24 hoursOne-time pairing tokens expire after 60 seconds and cannot be reusedServer sync is disabled by default. You must explicitly enable it in OptionsMemory SystemFSB's memory system stores navigation patterns and site intelligence to improve automation over time.All memory data (semantic, episodic, procedural) is stored locally in chrome.storage.localNo memory data is sent to any external serverMemory can be viewed and cleared at any time from the Options dashboardSite maps and navigation patterns are domain-specific and isolated from each otherSession Replay and ScreenshotsWhen an MCP client drives FSB, the extension records what the agent did so you can review or replay the run afterwards. These records stay on your device.Runs still in progress are held in Chrome's session storage so they survive the service worker being evicted; finished runs are written to chrome.storage.local through the same history store your own automation runs useRecords are redacted before they are stored. Values under credential-shaped keys, text typed into fields that look like password, one-time-code, or card inputs, and signed or token-bearing URL parameters are all replaced. Ordinary addresses and selectors are preserved, because replay does not work without themRecorded runs are kept for 30 days by default, adjustable between 1 and 365 days, and older ones are pruned by a daily job. Recording can be switched off entirely in Advanced SettingsScreenshot images are never written into that history. Only capture metadata such as dimensions and byte size is retainedScreenshots an MCP client requests are saved by the local service as files on your own disk with owner-only permissions and deleted automatically after seven days. The image is also handed back to the requesting client, and therefore to that client's modelAnonymous Usage TelemetryFSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.What we collectA random per-install UUID stored in chrome.storage.local under the key fsbInstallUuid. The UUID is generated locally and never tied to your identity.The name of the MCP client used (e.g. Claude Code, Cursor, Codex), drawn from a fixed allowlist.The model name used for a session (e.g. grok-4-fast, claude-opus-4), drawn from a fixed allowlist.Aggregate input/output token counts per session.The number of active FSB agents on your install (an integer count).What we do NOT collectPage URLs, hostnames, or browsing history.Prompts, instructions, task descriptions, or any natural-language text you send to your model provider.Page DOM, screenshots, page content, site API payloads, or AI responses.Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.Names, usernames, account handles, or any free-form identity fields.Email addresses, phone numbers, or contact information.Region (state-level) metricThe server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs.The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).RetentionRaw events are retained for 7 days. Daily rollups (one row per install per day, including the coarse region label) are retained for 365 days. Global aggregates (one row per day, no per-install dimension) are retained indefinitely so historical charts on /stats remain stable.How to opt outOpen the FSB Control Panel, scroll to Advanced Settings, and toggle Send anonymous usage data off. The change takes effect immediately; no further events will be sent from your install.How to erase your dataTo request erasure of all telemetry rows associated with your install (GDPR Article 17), look up your fsbInstallUuid in Chrome DevTools → Application → Storage → Extension storage, then send a single HTTP request:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Consent and Audit ControlsFSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. + 2026년 9월 28일부터 적용된, 2026년 9월 29일 업데이트 이전의 개인정보처리방침 아카이브 사본입니다.요약: FSB는 브라우저 안에서 실행되며, FSB 서버는 어떠한 브라우징 데이터도 수집하지 않습니다. AI 호출은 브라우저에서 선택한 제공업체로 직접 전송됩니다. API 키와 자격 증명은 로컬에서 암호화되고 메모리 데이터는 기기에 남습니다. 선택 사항인 로컬 에이전트 서비스를 설치하면 FSB는 사용자의 컴퓨터에서 루프백 연결로 실행되는 MCP 클라이언트, 그리고 이미 설치해 둔 코딩 에이전트 CLIs로도 제어할 수 있습니다. FSB를 위해 컴퓨터를 떠나는 유일한 데이터는 공개 페이지 /stats를 뒷받침하는, 수신 거부 가능한 익명 사용 통계뿐입니다. Remote Dashboard를 페어링하면 세션 중 일시적인 실시간 미리 보기 프레임이 릴레이를 거칠 수 있으나 저장되지는 않습니다. 모든 것이 오픈 소스이며 감사할 수 있습니다.데이터 수집FSB는 전적으로 브라우저 안에서 동작합니다. 자동화 작업을 시작하면 확장 프로그램이 활성 탭의 DOM(문서 객체 모델)을 검사할 수 있으며, 기능을 호출할 때는 브라우저 세션에서 동일 출처 사이트 API 요청을 보낼 수 있습니다.현재 세션을 넘어서는 브라우징 기록은 수집하거나 저장하지 않습니다DOM 데이터와 사이트 API 결과는 로컬에서 분석되며, 메모리에 명시적으로 저장하지 않는 한 각 자동화 단계 후 폐기됩니다방문한 페이지에서 개인 정보를 수집하지 않습니다사이트 API 기능FSB의 기능 계층은 이미 인증된 브라우저 세션을 사용하여 페이지 컨텍스트에서 사이트 자체의 1자 API를 호출할 수 있습니다. 이러한 요청은 Chrome에서 로컬로 실행됩니다. 대상 사이트에 대해 브라우저가 1자 쿠키나 사이트 인증을 첨부할 수 있으나, FSB는 쿠키, 토큰, CSRF 값, 요청 본문, 응답 본문을 FSB 서버로 반환하거나 저장하거나 기록하거나 전송하지 않습니다.기능 호출은 FSB의 동의 제어를 따르며, 편집된 로컬 감사 로그에만 기록됩니다. 기록 항목은 출처, 기능 슬러그, 메서드, 부수 효과 등급, 동의 결정, 결과입니다. 감사 로그는 호출 인수, 요청 본문, 응답 본문, 쿠키, 토큰, CSRF 값, 사이트 응답 페이로드를 결코 저장하지 않습니다.Chrome 권한 요청 항목: FSB웹 자동화를 실행하기 위해 FSB는 다음 권한을 Chrome 매니페스트에 선언합니다. 각 권한은 문서화된 목적으로만 사용되며, 어떤 권한을 근거로도 기기 밖으로 데이터를 보내지 않습니다.DOM 및 탭, activeTab, scripting, tabs, windows, sidePanel, and host permission <all_urls>: 활성 탭을 읽고 쓰며, 자동화 콘텐츠 스크립트를 삽입하고, 탭을 나열·전환하며, 사이드 패널을 렌더링합니다고급 자동화, debugger: 일반 Chrome DevTools Protocol 을 연결하여 좌표 기반 클릭, 드래그, 키 홀드 동작을 수행합니다. 이는 일반 DOM API로는 불가능합니다. webNavigation: 탐색 시작/종료 이벤트를 관찰하여 자동화가 적절한 시점을 기다리게 합니다로컬 저장소, storage, unlimitedStorage: 설정, 자격 증명, 결제 수단, 메모리를 기기의 chrome.storage.local에 저장합니다. 무제한 저장소는 기본 10 MB 할당량을 해제하여 메모리와 세션 로그가 한계에 부딪히지 않고 늘어날 수 있게 합니다UX 보조 기능, clipboardWrite: 자동화의 클립보드 복사 결과를 씁니다. alarms: 백그라운드 정리 작업을 예약합니다. offscreen: 서비스 워커가 오디오를 직접 캡처할 수 없으므로 음성 입력 레코더를 숨겨진 문서에서 실행합니다로컬 서비스, nativeMessaging: 아래 로컬 에이전트 서비스와 네이티브 메시징에서 설명하는 선택적 로컬 에이전트 서비스를 시작합니다. 방문한 페이지에 관한 정보는 이 채널을 통과하지 않습니다. system.memory: 설치된 총 RAM 용량만 읽어 FSB가 동시에 실행할 에이전트 수의 적절한 상한을 제안할 수 있게 합니다. 여유 메모리나 프로세서별 내역은 결코 읽지 않으며, 이 값이 확장 프로그램 밖으로 나가지도 않습니다음성 입력을 위한 마이크 접근 권한은 매니페스트에 선언되어 있지 않습니다. Chrome가 마이크 버튼을 처음 사용할 때 자체 권한 요청을 표시합니다.데이터 저장모든 설정과 데이터는 Chrome의 확장 프로그램 저장소에 로컬로 저장되며, 민감한 값은 아래 각 섹션에서 설명하는 대로 저장 시 암호화됩니다.구성은 다음 위치에 저장됩니다: chrome.storage.localAPI 키, 자격 증명, 결제 수단은 저장 전에 암호화되며, 각각의 세부 사항은 아래 해당 섹션에 있습니다세션 로그는 로컬에 저장되며 언제든지 삭제할 수 있습니다분석 데이터(작업 수, 성공률)는 기기에 남습니다외부 서비스FSB는 호스팅형 제공업체를 구성하고 사용할 때만 외부 AI 제공업체와 통신합니다. LM Studio를 사용하면 AI 요청은 로컬 OpenAI 호환 서버를 통해 기기 안에 머무릅니다. 제공업체 선택과 전송되는 데이터는 사용자가 통제합니다.호스팅형 API 호출은 선택한 제공업체(xAI, OpenAI, Anthropic, Google, OpenRouter)로만 이루어집니다LM Studio는 기기의 로컬 OpenAI 호환 서버를 사용하며 API 키가 필요하지 않습니다전송되는 데이터: 작업 설명, DOM 구조 요약, 동작 컨텍스트Remote Dashboard를 페어링하거나 기존 백그라운드 에이전트 동기화를 사용하면 선택적 릴레이 서버가 해당 세션의 WebSocket 메시지를 처리합니다. 실시간 미리 보기 프레임이 릴레이를 일시적으로 거칠 수 있으나, 다음 중 어느 것도 FSB 서버에 저장되지 않습니다: 페이지 콘텐츠, DOM 데이터, 브라우징 기록, 스크린샷, AI 프롬프트, AI 응답, 쿠키, 토큰, 사이트 API 페이로드. 이 기능은 수신 동의 방식이며, 본 방침의 나머지 부분은 이 목록을 반복하지 않고 참조합니다각 제공업체는 API 요청 처리 방식을 규율하는 자체 개인정보처리방침을 가지고 있습니다로컬 에이전트 서비스와 네이티브 메시징FSB는 사용자의 컴퓨터에서 실행되는 MCP 클라이언트로 제어할 수 있습니다. 이를 위해서는 사용자가 직접 설치하는 작은 로컬 에이전트 서비스가 필요하며, nativeMessaging 권한은 오직 확장 프로그램이 이를 시작할 수 있도록 하기 위해 존재합니다. Chrome는 로컬 프로그램을 직접 실행할 수 없으므로 이것이 유일하게 지원되는 방식입니다.메시징 호스트 io.github.fullselfbrowsing.fsb_native_host는 사용자가 fsb-mcp-server install --native-host를 실행하는 명시적 조치를 통해서만 등록됩니다. 확장 프로그램은 이를 설치할 수 없으며, 등록 항목에는 허용된 확장 프로그램 출처가 하나만 지정됩니다이 교환은 닫힌 스키마입니다. FSB는 버전 번호, wake 또는 bootstrap 중 하나의 동작, 그리고 로컬에서 생성한 상관 식별자를 전송하고, 그 식별자와 함께 고정된 목록에서 선택된 결과 및 사유를 돌려받습니다. 추가 필드는 즉시 거부되며 메시지는 4 KB로 제한됩니다. 이 스키마에는 URL, 탭, 페이지 콘텐츠, DOM 데이터가 전혀 존재하지 않습니다시작에 성공하면 페어링 코드가 반환됩니다. 이는 기기에서 생성되어 소유자만 접근할 수 있는 권한으로 저장되며 서비스가 시작될 때마다 교체되는 루프백 인증 비밀입니다. 사용자나 브라우징에 관한 정보는 담고 있지 않습니다이 서비스는 사용자의 컴퓨터에서만 수신 대기합니다. 확장 프로그램 브리지는 루프백이 아닌 주소로의 바인딩을 거부하며, MCP 엔드포인트는 기본적으로, 그리고 127.0.0.1가 시작하는 경우에는 항상 FSB에 바인딩합니다이 서비스는 자체 디렉터리에 운영 기록만 보관합니다. 고정된 필드 목록으로 제한된 위임 실행 결과 진단 로그와 프로세스 감독 저널입니다. 작업 텍스트, 환경 변수 값, 바이너리 경로, 오류 텍스트는 둘 중 어느 곳에도 기록되지 않으며, 자격 증명 형태의 값을 담은 항목은 기록되지 않고 폐기됩니다호스트가 설치되어 있지 않으면 이 기능은 단순히 시작되지 않습니다. 아무것도 전송되지 않으며 확장 프로그램의 나머지 기능은 정상적으로 동작합니다위임된 CLI 에이전트FSB는 이미 설치해 둔 코딩 에이전트 명령줄 도구(현재 Claude Code 또는 Grok Build)에 작업을 넘길 수 있습니다. FSB는 해당 프로그램을 사용자의 컴퓨터에서 실행합니다. 작업 내용이 FSB 서버를 거쳐 전달되는 일은 없습니다.작업 텍스트는 도구의 표준 입력으로만 전달되며 다른 어떤 경로로도 전달되지 않습니다. FSB는 실행할 때마다 이를 확인합니다. 작업 내용이 명령줄, 작업 디렉터리, 환경 변수, 파일 경로 중 어디에라도 나타나면 실행을 시작하지 않고 중단합니다. 작업 크기는 64 KB로 제한됩니다실행 중에 이 도구는 루프백 연결을 통해 FSB를 다시 호출하여 브라우저를 제어합니다. 그 과정에서 읽은 것, 즉 페이지 텍스트, DOM 스냅숏, 스프레드시트 값, 스크린샷은 해당 도구로 반환되며 따라서 그 공급업체에 전달됩니다. 대응은 Anthropic가 Claude Code, xAI가 Grok Build실행에는 해당 도구에 이미 로그인된 계정이 사용됩니다. FSB는 사용자의 Anthropic 또는 xAI 자격 증명을 보관하거나 읽거나 전송하지 않으며, 사용량과 요금은 해당 공급업체의 요금제를 따릅니다Grok Build는 FSB가 사용자 소유와 분리해 관리하는 프로필 디렉터리 안에서 실행됩니다. 그곳에 저장되는 로그인 자격 증명은 Grok 도구가 자체 로그인 절차 중에 직접 기록합니다FSB가 제공하는 에이전트 지침은 비밀번호, CVV 값, 결제 카드 정보, 저장된 자격 증명이 프롬프트, 설명, 로그, 도구 인수에 절대 들어가서는 안 된다고 명시합니다Google Sheets 읽기와 입력FSB는 이미 인증된 브라우저 세션에서 스프레드시트 화면을 조작하여 Google Sheets의 범위를 읽고 입력할 수 있습니다.범위를 읽으면 해당 셀 값이, FSB가 사용자를 대신해 읽는 다른 페이지 콘텐츠와 똑같이 선택한 모델로 전송됩니다. 요청한 범위만 읽으며 스프레드시트 전체를 내보내지 않습니다스프레드시트 내용은 FSB의 로컬 세션 기록에 무언가가 기록되기 전에 제거됩니다. 남는 것은 규모를 나타내는 개수(행, 열, 값의 수)뿐입니다. 스프레드시트 주소, 시트 이름, 셀 참조, 모든 셀 값은 폐기됩니다어떤 이유로든 이 필터링을 레코드에 적용할 수 없으면 해당 레코드는 저장되지 않고 폐기됩니다Remote Dashboard 및 PhantomStream 실시간 미리 보기Remote Dashboard를 페어링하면 FSB는 PhantomStream을 사용하여 활성 브라우저 탭의 실시간 미리 보기를 표시할 수 있습니다. 미리 보기는 픽셀 대신 구조화된 DOM 데이터를 전송합니다. 최초 스냅숏, MutationObserver 차이, 스크롤 위치, 자동화 오버레이, 대화 상자 상태, 미디어 재생 상태, 원격 제어 메시지가 WebSocket을 통해 전달됩니다.릴레이는 이러한 실시간 미리 보기 프레임을 페어링된 대시보드 세션에만 전달하며, 미리 보기 스트림의 어떤 내용도 FSB 서버에 저장되지 않습니다. 위의 외부 서비스 목록을 참조하십시오FSB는 PhantomStream 입력 마스킹(maskInputs: true)을 활성화하므로 비밀번호와 폼 컨트롤 값은 캡처된 페이지를 떠나기 전에 가려집니다대시보드 뷰어는 미러링된 콘텐츠를 스크립트가 없는 샌드박스에서 렌더링하며, 표시 전에 미러링된 DOM과 CSS를 정화합니다현재 대시보드 모드에서 이미지, 동영상, 오디오는 참조로 미러링됩니다. 미디어 바이트는 릴레이를 거치지 않으며, 뷰어가 허용된 공개 HTTPS 자산이나 미디어 URL을 직접 가져올 수 있습니다. 반면 비공개, 내부, 비 HTTPS, 그 밖에 차단된 출처는 PhantomStream의 실패 시 차단 정책에 따라 자리 표시자로 대체됩니다제3자 추적 없음FSB는 어떠한 제3자 분석 도구, 광고 추적기, 교차 사이트 핑거프린팅도 포함하지 않습니다. 쿠키가 없으며, 사용자가 직접 구성한 AI 제공업체 APIs 외에는 제3자 스크립트도 없습니다. FSB가 본사로 보내는 유일한 1자 데이터는 아래에 설명한 수신 거부 가능한 익명 사용 통계이며, 오직 공개 /stats 대시보드를 뒷받침하는 데에만 사용됩니다.API 키사용자의 API 키는 저장 전에 AES-GCM을 사용해 로컬에서 암호화됩니다. 사용자가 구성한 AI 제공업체 외에는 어디에도 전송되지 않으며, 전송 시에도 API 요청의 인증 헤더로만 사용됩니다.키는 Chrome 저장소에 암호화된 상태로 보관됩니다복호화는 API 호출을 수행할 때 메모리 안에서만 이루어집니다키는 기록되거나 내보내지거나 공유되지 않습니다자동 비밀번호FSB는 로그인 자격 증명을 기기에 암호화해 저장하는 선택적 자격 증명 관리자를 포함합니다. 비밀번호는 AI 모델에 절대 노출되지 않습니다. 콘텐츠 스크립트가 AI를 완전히 우회하여 페이지에 직접 입력합니다.자격 증명은 256비트 키와 AES-GCM 키 파생을 사용하는 PBKDF2으로 저장 시 암호화됩니다AI가 페이지를 분석할 때 비밀번호 필드의 값은 [hidden]으로 대체됩니다. 실제 비밀번호는 어떤 AI 프롬프트에도 포함되지 않습니다자동 입력은 콘텐츠 스크립트가 값을 DOM에 직접 써넣는 방식으로 이루어지며, 자격 증명 흐름에 AI는 전혀 관여하지 않습니다자격 증명 목록 화면에는 사용자 이름과 도메인만 표시됩니다. 비밀번호는 자동 입력에 필요할 때만 개별적으로 복호화됩니다자격 증명은 도메인별로 저장되며 상위 도메인으로 대체됩니다(예: accounts.google.com는 google.com에서 상속)결제 수단FSB는 결제 화면 자동 입력을 위해 카드 정보를 기기에 저장하는 선택적 결제 수단 볼트를 포함합니다. 카드는 로그인 자격 증명과 동일한 암호화 및 AI 격리로 취급되며, 전체 카드 번호는 어떤 AI 모델에도 전송되지 않습니다.카드 정보(번호, 유효기간, 소유자, 우편번호)는 자격 증명과 동일한 볼트 파생 키를 사용하는 AES-GCM으로 저장 시 암호화됩니다AI가 결제 페이지를 분석할 때, 감지된 카드 번호 필드의 값은 프롬프트가 구성되기 전에 [hidden]으로 대체됩니다. 카드 번호, CVV, 유효기간은 어떤 AI 프롬프트에도 포함되지 않습니다자동 입력은 콘텐츠 스크립트가 페이지의 DOM 필드에 직접 써넣는 방식으로 이루어지며 AI를 완전히 우회합니다목록 화면에는 카드 별칭과 마지막 네 자리만 표시됩니다. 전체 번호는 입력하는 순간에만 메모리에서 복호화됩니다MCP 클라이언트는 use_payment_method를 통해 결제 입력을 요청할 수 있으나, 카드 정보가 페이지에 기록되기 전에 확장 프로그램 내 확인 창이 사용자에게 표시됩니다CVV는 카드별로 사용자가 동의하지 않는 한 저장되지 않으며, 동의한 경우에도 나머지 기록과 함께 암호화됩니다음성 입력FSB는 프롬프트 입력란에 선택적 마이크 입력을 제공합니다. 기본 제공자는 전적으로 브라우저 안에서 동작하며, 더 높은 정확도를 원하면 설정에서 선택적 OpenAI Whisper 대체 수단을 활성화할 수 있습니다.기본 제공자: 브라우저의 네이티브 SpeechRecognition API. 오디오는 Chrome가 처리하며 FSB를 통해 기기를 떠나지 않습니다선택적 Whisper 제공자: sttProvider가 whisper로 설정되고 OpenAI 키가 구성되어 있으면, 녹음된 오디오 조각이 브라우저에서 OpenAI의 전사 엔드포인트로 직접 업로드됩니다. FSB는 그 오디오를 보거나 저장하지 않습니다마이크는 마이크 버튼을 누르고 있거나 켜 둔 동안에만 활성화됩니다. Chrome가 처음 사용할 때 권한을 요청하며, FSB는 확장 프로그램 매니페스트에서 마이크 접근 권한을 요청하지 않습니다전사 결과는 프롬프트 입력란에만 삽입되며, 사용자가 보내기로 선택한 활성 AI 요청 밖으로 기록되거나 저장되거나 전송되지 않습니다마이크 버튼을 사용하지 않거나, Chrome 확장 프로그램 저장소에서 선택적 Whisper 제공자를 지우면 음성 기능을 완전히 비활성화할 수 있습니다프롬프트 인젝션 방지웹 페이지에는 AI 에이전트를 탈취하도록 설계된 숨은 텍스트가 포함될 수 있습니다. FSB는 AI가 오직 사용자의 지시만 따르고 페이지 콘텐츠에 삽입된 지시는 따르지 않도록 다층 방어를 구현합니다.모든 페이지 콘텐츠는 [PAGE_CONTENT] 경계 표시로 감싸이며, AI는 이 표시 안에서 발견된 지시를 절대 따르지 않도록 지시받습니다정화 엔진이 알려진 인젝션 패턴(예: "이전 지시를 무시하라", 가짜 시스템 프롬프트, 재정의 시도)을 AI에 도달하기 전에 모든 페이지 콘텐츠에서 제거합니다AI가 생성한 동작은 실행 전에 검증됩니다. 위험한 URL(javascript:, data:)과 스크립트 인젝션 시도는 차단됩니다알려진 도구의 엄격한 고정 허용 목록만 실행할 수 있습니다. AI는 임의의 동작을 만들어 내거나 호출할 수 없습니다콘텐츠 크기는 값당 500자, 프롬프트 전체 15K로 제한되어 페이로드 전달을 억제합니다웹사이트가 삽입하는 보이지 않는 유니코드 제어 문자는 처리 전에 제거됩니다백그라운드 에이전트와 서버 동기화지원 중단 버전: v0.9.45rc1. FSB의 내장 백그라운드 에이전트는 OpenClaw 및 Claude Routines로 대체되었으며, 원격 제어는 이제 Sync 탭에서 처리합니다. 아래 고지는 아직 v0.9.44 이하를 사용하는 분들을 위해 유지합니다. 현재 빌드에서는 Sync 세션을 페어링할 때만 릴레이 서버에 접속합니다.기존 백그라운드 에이전트 서버 동기화를 사용하기로 선택하거나 Remote Dashboard를 페어링하면, 릴레이 서버가 확장 프로그램과 대시보드 사이의 통신을 중계합니다.서버에 저장되는 항목: 에이전트 정의(이름, 일정, 대상 URL), 실행 지표(토큰 수, 비용, 소요 시간, 성공/실패 상태), 세션 페어링 토큰그 실행 메타데이터 외에, 방문한 페이지에서 파생된 것은 서버에 저장되지 않습니다. 위의 외부 서비스 목록을 참조하십시오인증에는 로컬에서 생성된 해시 키와 24시간 후 만료되는 세션 토큰을 사용합니다일회용 페어링 토큰은 60초 후 만료되며 재사용할 수 없습니다서버 동기화는 기본적으로 비활성화되어 있습니다. 옵션에서 명시적으로 활성화해야 합니다메모리 시스템FSB의 메모리 시스템은 시간이 지날수록 자동화를 개선하기 위해 탐색 패턴과 사이트 인텔리전스를 저장합니다.모든 메모리 데이터(의미, 일화, 절차)는 다음 위치에 로컬로 저장됩니다: chrome.storage.local어떤 메모리 데이터도 외부 서버로 전송되지 않습니다메모리는 옵션 대시보드에서 언제든지 확인하고 삭제할 수 있습니다사이트 맵과 탐색 패턴은 도메인별로 구분되어 서로 격리됩니다세션 리플레이와 스크린샷MCP 클라이언트가 FSB를 제어하면, 확장 프로그램은 나중에 실행 내용을 검토하거나 재생할 수 있도록 에이전트의 동작을 기록합니다. 이 기록은 기기에 남습니다.진행 중인 실행은 Chrome의 세션 저장소에 보관되어 서비스 워커가 종료되어도 유지됩니다. 완료된 실행은 사용자 본인의 자동화 실행과 동일한 기록 저장소를 통해 chrome.storage.local에 기록됩니다기록은 저장 전에 편집됩니다. 자격 증명 형태의 키 아래에 있는 값, 비밀번호·일회용 코드·카드 입력으로 보이는 필드에 입력된 텍스트, 서명되었거나 토큰을 포함한 URL 매개변수는 모두 대체됩니다. 일반적인 주소와 선택자는 그것이 없으면 리플레이가 동작하지 않으므로 보존됩니다기록된 실행은 기본적으로 30일간 보관되며 1일에서 365일 사이로 조정할 수 있고, 오래된 것은 매일 실행되는 작업이 정리합니다. 고급 설정에서 기록 기능을 완전히 끌 수 있습니다스크린샷 이미지는 그 기록에 저장되지 않습니다. 크기와 바이트 수 같은 캡처 메타데이터만 유지됩니다MCP 클라이언트가 요청한 스크린샷은 로컬 서비스가 소유자만 접근할 수 있는 권한으로 사용자 디스크에 파일로 저장하며 7일 후 자동으로 삭제합니다. 이 이미지는 요청한 클라이언트에도 반환되므로 해당 클라이언트의 모델에도 전달됩니다익명 사용 통계FSB v0.9.69에서는 프로젝트가 집계된 도입 수치를 공개할 수 있도록 수신 거부 가능한 익명 사용 통계 파이프라인을 도입했습니다(/stats 참조). 이 과정에서 사용자가 탐색하는 페이지에는 전혀 접근하지 않습니다. 유일한 위치 신호는 서버가 수집 시점에 요청 IP에서 도출하는 대략적인 국가/주 라벨이며(페이지 콘텐츠에서 도출하지 않습니다) 집계 형태로만 공개됩니다. 아래 지역(주 단위) 지표를 참조하십시오. 통계는 기본적으로 켜져 있으나 토글 하나로 끌 수 있으며, 설치별 데이터는 요청 시 삭제할 수 있습니다.수집하는 항목설치마다 무작위로 생성되는 UUID. chrome.storage.local에 키 fsbInstallUuid로 저장됩니다. 이 UUID는 로컬에서 생성되며 사용자의 신원과 결코 연결되지 않습니다.사용한 MCP 클라이언트의 이름(예: Claude Code, Cursor, Codex). 고정된 허용 목록에서 가져옵니다.세션에 사용한 모델 이름(예: grok-4-fast, claude-opus-4). 고정된 허용 목록에서 가져옵니다.세션당 입력/출력 토큰 수의 합계입니다.설치된 환경에서 활성화된 FSB 에이전트의 수(정수)입니다.수집하지 않는 항목페이지 URLs, 호스트 이름, 브라우징 기록.프롬프트, 지시, 작업 설명 등 모델 제공업체로 보내는 모든 자연어 텍스트.페이지 DOM, 스크린샷, 페이지 콘텐츠, 사이트 API 페이로드, AI 응답.평문 IP 주소. 서버는 요청 IP를 일시적으로, 그리고 인라인으로 정확히 세 가지 용도에만 사용합니다. 속도 제한을 위한 매일 교체되는 솔트 해시, 대략적인 국가/주 라벨 도출(아래 참조), 그리고 주소가 IPv4인지 IPv6인지 기록하는 것입니다. 그 후 즉시 폐기합니다. 평문 IP는 저장하거나 기록하지 않습니다.이름, 사용자명, 계정 핸들 등 자유 형식의 신원 필드.이메일 주소, 전화번호, 연락처 정보.지역(주 단위) 지표서버는 수집 시점에 요청 IP로부터 대략적인 국가 및 미국 주(하위 행정구역) 라벨을 도출합니다. 이때 자체 호스팅하는 DB-IP IP-to-City Lite 데이터셋과 자체 조회를 사용하며, 실시간 제3자 위치 서비스는 사용하지 않고 MaxMind도 결코 사용하지 않습니다. 평문 IP는 인라인으로 소비된 뒤 폐기되며, 도출된 라벨과 주소 체계(IPv4 또는 IPv6)만 보관됩니다.지역은 k≥5 익명성 하한을 적용한 집계 형태로만 한정하여 공개됩니다. 고유 설치 수가 5 미만인 주는 모두 하나의 "기타" 묶음으로 합쳐지며(합쳐진 수마저 5 미만이면 완전히 억제됩니다), 공개되는 주 라벨이 설치 5건 미만을 나타내는 일은 결코 없습니다.대략적인 라벨은 원시 이벤트(7일 보관 기간이 지나면 삭제)와 설치의 일별 집계에 저장되며, 일별 집계는 /stats가 각 설치를 가장 최근 지역 기준으로 한 번만 셀 수 있도록 365일간 보관됩니다. 이 라벨은 IP 주소, 도시, 좌표가 아닙니다. 삭제를 요청하면 설치별 나머지 데이터와 함께 삭제되며, 위의 k≥5 하한 뒤에서만 공개됩니다. 위치 데이터 제공: DB-IP (https://db-ip.com).보관 기간원시 이벤트는 7일간 보관합니다. 일별 집계(설치당 하루 한 행, 대략적인 지역 라벨 포함)는 365일간 보관합니다. 전역 집계(하루 한 행, 설치별 구분 없음)는 /stats의 과거 차트가 안정적으로 유지되도록 무기한 보관합니다.수신 거부 방법FSB 제어판을 열고 고급 설정으로 스크롤한 뒤 Send anonymous usage data를 끄십시오. 변경 사항은 즉시 적용되며, 이후로는 해당 설치에서 어떤 이벤트도 전송되지 않습니다.데이터 삭제 방법설치와 연결된 모든 통계 행의 삭제를 요청하려면(GDPR 제17조), fsbInstallUuid에서 Chrome DevTools → 애플리케이션 → 저장소 → 확장 프로그램 저장소 경로로 HTTP 요청을 한 번 보내십시오:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forget제한적 사용 확약FSB의 익명 사용 통계는 오직 full-selfbrowsing.com/stats에 공개적으로 표시되는 집계 사용 통계를 계산하는 데에만 사용됩니다. 이 데이터는 판매되거나, 제3자와 공유되거나, 광고에 사용되거나, 어떤 머신러닝 모델의 학습에 사용되지 않습니다. 이 약속은 Chrome Web Store의 Limited Use 요건을 충족합니다.공개 집계 지표이 통계 파이프라인에서 도출한 집계 지표를 /stats에 공개합니다. 개수와 합계만 표시하며, 설치별 행은 결코 공개하지 않습니다.동의 및 감사 제어FSB의 자동화 태세는 수신 거부 방식입니다. 차단 목록에 없는 출처는 사용자가 구성할 수 있는 전역 기본값을 상속하며 현재 기본값은 자동입니다. 출처별 명시 정책으로 특정 출처를 끄기, 확인, 자동 중 하나로 설정할 수 있습니다. 감사 로그 보관은 로컬에서 제한적으로 이루어지며, 제어판에서 내보내기와 삭제를 할 수 있습니다.서비스 차단 목록은 여전히 강제 차단입니다. 차단된 출처는 전역 기본값이나 저장된 출처별 정책과 관계없이 활성화할 수 없습니다.민감한 출처는 자동 상태에서 읽기를 수행할 수 있으나, 쓰기는 실행 전에 출처별 변경 동의를 다시 확인합니다. 민감하지 않은 출처는 제어판의 동의 및 감사 섹션에서 수신 거부하거나 확인 모드로 변경할 수 있습니다.모든 기능 호출은 편집된 추가 전용 로컬 감사 로그에 기록됩니다. 인수, 토큰, 쿠키, 응답 본문은 결코 저장되지 않습니다.오픈 소스FSB는 MIT 라이선스로 완전히 공개된 오픈 소스입니다. 모든 코드를 직접 검토하여 이러한 개인정보 보호 주장을 확인할 수 있습니다. 소스 코드는 GitHub에서 확인할 수 있습니다.본 방침의 변경본 방침이 업데이트되면 이 페이지 상단의 "최종 업데이트" 날짜에 변경 사항이 반영됩니다. 중요한 변경 사항은 프로젝트의 GitHub 릴리스 노트에도 기재됩니다.문의본 개인정보처리방침이나 FSB의 데이터 처리에 대해 궁금한 점이 있으면 GitHub Issues에 이슈를 등록해 주십시오. + + src/app/pages/privacy/privacy-history-archive.component.html + 7,238 + + August 2026v0.9.91 — Local Agent Service and Native Messaging, Delegated CLI Agents, Google Sheets, Session Replay and Screenshots (full archived text) 2026년 8월v0.9.91 — 로컬 에이전트 서비스와 네이티브 메시징, 위임된 CLI 에이전트, Google Sheets, 세션 리플레이와 스크린샷 (아카이브 전문) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 242,245 @@ -9559,7 +9579,7 @@ https://full-selfbrowsing.com/api/telemetry/forget제한적 사용 확약FSB의 익명 사용 통계는 오직 full-selfbrowsing.com/stats에 공개적으로 표시되는 집계 사용 통계를 계산하는 데에만 사용됩니다. 이 데이터는 판매되거나, 제3자와 공유되거나, 광고에 사용되거나, 어떤 머신러닝 모델의 학습에 사용되지 않습니다. 이 약속은 Chrome Web Store의 Limited Use 요건을 충족합니다.공개 집계 지표이 통계 파이프라인에서 도출한 집계 지표를 /stats에 공개합니다. 개수와 합계만 표시하며, 설치별 행은 결코 공개하지 않습니다.동의 및 감사 제어FSB의 자동화 태세는 수신 거부 방식입니다. 차단 목록에 없는 출처는 사용자가 구성할 수 있는 전역 기본값을 상속하며 현재 기본값은 자동입니다. 출처별 명시 정책으로 특정 출처를 끄기, 확인, 자동 중 하나로 설정할 수 있습니다. 감사 로그 보관은 로컬에서 제한적으로 이루어지며, 제어판에서 내보내기와 삭제를 할 수 있습니다.서비스 차단 목록은 여전히 강제 차단입니다. 차단된 출처는 전역 기본값이나 저장된 출처별 정책과 관계없이 활성화할 수 없습니다.민감한 출처는 자동 상태에서 읽기를 수행할 수 있으나, 쓰기는 실행 전에 출처별 변경 동의를 다시 확인합니다. 민감하지 않은 출처는 제어판의 동의 및 감사 섹션에서 수신 거부하거나 확인 모드로 변경할 수 있습니다.모든 기능 호출은 편집된 추가 전용 로컬 감사 로그에 기록됩니다. 인수, 토큰, 쿠키, 응답 본문은 결코 저장되지 않습니다.오픈 소스FSB는 MIT 라이선스로 완전히 공개된 오픈 소스입니다. 모든 코드를 직접 검토하여 이러한 개인정보 보호 주장을 확인할 수 있습니다. 소스 코드는 GitHub에서 확인할 수 있습니다.본 방침의 변경본 방침이 업데이트되면 이 페이지 상단의 "최종 업데이트" 날짜에 변경 사항이 반영됩니다. 중요한 변경 사항은 프로젝트의 GitHub 릴리스 노트에도 기재됩니다.문의본 개인정보처리방침이나 FSB의 데이터 처리에 대해 궁금한 점이 있으면 GitHub Issues에 이슈를 등록해 주십시오. src/app/pages/privacy/privacy-history-archive.component.html - 7,238 + 246,477 @@ -9567,7 +9587,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026년 5월 – 7월v0.9.90 — 사이트 API 기능, Remote Dashboard 및 PhantomStream, 결제 수단, 음성 입력, 지역 지표 (아카이브 전문) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 481,484 @@ -9579,7 +9599,7 @@ https://full-selfbrowsing.com/api/telemetry/forget제한적 사용 확약FSB의 익명 사용 통계는 오직 full-selfbrowsing.com/stats에 공개적으로 표시되는 집계 사용 통계를 계산하는 데에만 사용됩니다. 이 데이터는 판매되거나, 제3자와 공유되거나, 광고에 사용되거나, 어떤 머신러닝 모델의 학습에 사용되지 않습니다. 이 약속은 Chrome Web Store의 Limited Use 요건을 충족합니다.공개 집계 지표이 통계 파이프라인에서 도출한 집계 지표를 /stats에 공개합니다. 개수와 합계만 표시하며, 설치별 행은 결코 공개하지 않습니다.동의 및 감사 제어FSB의 자동화 태세는 수신 거부 방식입니다. 차단 목록에 없는 출처는 사용자가 구성할 수 있는 전역 기본값을 상속하며 현재 기본값은 자동입니다. 출처별 명시 정책으로 특정 출처를 끄기, 확인, 자동 중 하나로 설정할 수 있습니다. 감사 로그 보관은 로컬에서 제한적으로 이루어지며, 제어판에서 내보내기와 삭제를 할 수 있습니다.서비스 차단 목록은 여전히 강제 차단입니다. 차단된 출처는 전역 기본값이나 저장된 출처별 정책과 관계없이 활성화할 수 없습니다.민감한 출처는 자동 상태에서 읽기를 수행할 수 있으나, 쓰기는 실행 전에 출처별 변경 동의를 다시 확인합니다. 민감하지 않은 출처는 제어판의 동의 및 감사 섹션에서 수신 거부하거나 확인 모드로 변경할 수 있습니다.모든 기능 호출은 편집된 추가 전용 로컬 감사 로그에 기록됩니다. 인수, 토큰, 쿠키, 응답 본문은 결코 저장되지 않습니다.오픈 소스FSB 는 MIT 라이선스로 완전히 공개된 오픈 소스입니다. 모든 코드를 직접 검토하여 이러한 개인정보 보호 주장을 확인할 수 있습니다. 소스 코드는 GitHub를 주십시오.본 방침의 변경본 방침이 업데이트되면 이 페이지 상단의 "최종 업데이트" 날짜에 변경 사항이 반영됩니다. 중요한 변경 사항은 프로젝트의 GitHub 릴리스 노트에도 기재됩니다.문의본 개인정보처리방침이나 FSB의 데이터 처리에 대해 궁금한 점이 있으면 GitHub Issues를 주십시오. src/app/pages/privacy/privacy-history-archive.component.html - 7,198 + 485,676 @@ -9587,7 +9607,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026년 3월v9.0.2 — 백그라운드 에이전트, 메모리 시스템, 서버 동기화, 익명 사용 통계, 법적 태세 (아카이브 전문) src/app/pages/privacy/privacy-history-archive.component.html - 202,205 + 680,683 @@ -9599,7 +9619,7 @@ https://full-selfbrowsing.com/api/telemetry/forget제한적 사용 확약FSB의 익명 사용 통계는 오직 full-selfbrowsing.com/stats에 공개적으로 표시되는 집계 사용 통계를 계산하는 데에만 사용됩니다. 이 데이터는 판매되거나, 제3자와 공유되거나, 광고에 사용되거나, 어떤 머신러닝 모델의 학습에 사용되지 않습니다. 이 약속은 Chrome Web Store의 Limited Use 요건을 충족합니다.공개 집계 지표이 통계 파이프라인에서 도출한 집계 지표를 /stats에 공개합니다. 개수와 합계만 표시하며, 설치별 행은 결코 공개하지 않습니다.법적 태세와 동의 모델FSB의 자동화 태세, 감사 로그 보관, 출처별 동의 모델은 명시적인 제품 제어 항목으로 제시되었습니다. FSB는 사용자가 명시적으로 허용하기 전에는 어떤 출처에서도 아무것도 하지 않으며, 자동 읽기 권한이 쓰기 권한을 의미하는 일은 결코 없고, 보수적인 서비스 차단 목록이 민감한 범주(금융 및 정부 서비스)에서의 자동화를 원천 차단합니다.출처별 동의는 기본적으로 꺼져 있습니다. 자동 실행과 쓰기(변경) 권한은 제어판의 동의 및 감사 섹션에서 관리하는 별개의 명시적 동의 항목입니다.모든 기능 호출은 편집된 추가 전용 로컬 감사 로그에 기록됩니다. 인수, 토큰, 쿠키, 응답 본문은 결코 저장되지 않습니다.서비스 차단 목록은 민감한 출처를 활성화할 수 없게 만들며, 이는 인터페이스에서만이 아니라 기능 게이트에서 강제됩니다.오픈 소스FSB 는 MIT 라이선스로 완전히 공개된 오픈 소스입니다. 모든 코드를 직접 검토하여 이러한 개인정보 보호 주장을 확인할 수 있습니다. 소스 코드는 GitHub를 주십시오.본 방침의 변경본 방침이 업데이트되면 이 페이지 상단의 "최종 업데이트" 날짜에 변경 사항이 반영됩니다. 중요한 변경 사항은 프로젝트의 GitHub 릴리스 노트에도 기재됩니다.문의본 개인정보처리방침이나 FSB의 데이터 처리에 대해 궁금한 점이 있으면 GitHub Issues를 주십시오. src/app/pages/privacy/privacy-history-archive.component.html - 206,345 + 684,823 @@ -9607,7 +9627,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026년 2월v0.9 — 최초 개인정보처리방침 (아카이브 전문) src/app/pages/privacy/privacy-history-archive.component.html - 349,352 + 827,830 @@ -9615,7 +9635,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026년 2월 시점의 최초 개인정보처리방침 아카이브 사본입니다. 이 스냅숏은 원래의 정적 쇼케이스 페이지에서 페이지 구성 요소를 제거하고 방침 본문을 보존하여 재현한 것입니다.데이터 수집FSB는 전적으로 브라우저 안에서 동작합니다. 확장 프로그램은 자동화 작업을 시작할 때 현재 활성 탭의 DOM(문서 객체 모델)에만 접근합니다.현재 세션을 넘어서는 브라우징 기록은 수집하거나 저장하지 않습니다DOM 데이터는 로컬에서 분석되며 각 자동화 단계 후 폐기됩니다방문한 페이지에서 개인 정보를 수집하지 않습니다데이터 저장모든 설정과 데이터는 Chrome의 확장 프로그램 저장소에 저장됩니다. FSB 가 AES-GCM 암호화를 API 키 같은 민감한 데이터에 사용합니다.구성은 다음 위치에 저장됩니다: chrome.storage.localAPI 키는 저장 전에 다음 방식으로 암호화됩니다: AES-GCM세션 로그는 로컬에 저장되며 언제든지 삭제할 수 있습니다분석 데이터(작업 수, 성공률)는 기기에 남습니다외부 서비스FSB는 사용자가 외부 AI 제공업체를 구성하고 사용할 때만 그들과 통신합니다. 제공업체 선택과 전송되는 데이터는 사용자가 통제합니다.API 호출은 선택한 제공업체(xAI, OpenAI, Anthropic, Google)로만 이루어집니다전송되는 데이터: 작업 설명, DOM 구조 요약, 동작 컨텍스트No data is sent to FSB 서버로 전송되는 데이터는 없습니다. 애초에 그런 서버가 없습니다각 제공업체는 API 요청 처리 방식을 규율하는 자체 개인정보처리방침을 가지고 있습니다No TrackingFSB는 어떠한 분석 도구, 통계 수집, 추적 서비스도 포함하지 않습니다. 쿠키도, 핑거프린팅도 없으며, 사용자가 직접 구성한 AI 제공업체 API 외에는 제3자 스크립트도 없습니다.API 키사용자의 API 키는 저장 전에 AES-GCM 을 사용해 로컬에서 암호화됩니다. 사용자가 구성한 AI 제공업체 외에는 어디에도 전송되지 않으며, 전송 시에도 API 요청의 인증 헤더로만 사용됩니다.키는 Chrome storage복호화는 API 호출을 수행할 때 메모리 안에서만 이루어집니다키는 기록되거나 내보내지거나 공유되지 않습니다Auto-Passwords BetaFSB는 로그인 자격 증명을 기기에 암호화해 저장하는 선택적 자격 증명 관리자를 포함합니다. 비밀번호는 AI 모델에 절대 노출되지 않으며, 콘텐츠 스크립트가 AI를 완전히 우회하여 페이지에 직접 입력합니다.자격 증명은 256비트 키와 AES-GCM 키 파생을 사용하는 PBKDF2 으로 저장 시 암호화됩니다AI가 페이지를 분석할 때 비밀번호 필드의 값은 [hidden]으로 대체됩니다. 실제 비밀번호는 어떤 AI 프롬프트에도 포함되지 않습니다자동 입력은 콘텐츠 스크립트가 값을 DOM에 직접 써넣는 방식으로 이루어지며, 자격 증명 흐름에 AI는 전혀 관여하지 않습니다자격 증명 목록 화면에는 사용자 이름과 도메인만 표시됩니다. 비밀번호는 자동 입력에 필요할 때만 개별적으로 복호화됩니다자격 증명은 도메인별로 저장되며 상위 도메인으로 대체됩니다(예: accounts.google.com 는 google.com)로만 이루어집니다프롬프트 인젝션 방지웹 페이지에는 AI 에이전트를 탈취하도록 설계된 숨은 텍스트가 포함될 수 있습니다. FSB 는 AI가 오직 사용자의 지시만 따르고 페이지 콘텐츠에 삽입된 지시는 따르지 않도록 다층 방어를 구현합니다.모든 페이지 콘텐츠는 [PAGE_CONTENT] 경계 표시로 감싸이며, AI는 이 표시 안에서 발견된 지시를 절대 따르지 않도록 지시받습니다정화 엔진이 알려진 인젝션 패턴(예: "이전 지시를 무시하라", 가짜 시스템 프롬프트, 재정의 시도)을 AI에 도달하기 전에 모든 페이지 콘텐츠에서 제거합니다AI가 생성한 동작은 실행 전에 검증됩니다. 위험한 URL(javascript:, data:)과 스크립트 인젝션 시도는 차단됩니다알려진 30개 이상 도구의 엄격한 허용 목록만 실행할 수 있습니다. AI는 임의의 동작을 만들어 내거나 호출할 수 없습니다콘텐츠 크기는 값당 500자, 프롬프트 전체 15K로 제한되어 페이로드 전달을 억제합니다웹사이트가 삽입하는 보이지 않는 유니코드 제어 문자는 처리 전에 제거됩니다오픈 소스FSB 는 MIT 라이선스로 완전히 공개된 오픈 소스입니다. 모든 코드를 직접 검토하여 이러한 개인정보 보호 주장을 확인할 수 있습니다. 소스 코드는 GitHub를 주십시오.View the source code on GitHub요약FSB는 데이터를 로컬에서 처리하고, 민감한 정보를 암호화하고, 비밀번호를 AI 모델에 노출하지 않으며, 프롬프트 인젝션 공격을 방어하고, 사용자가 선택한 AI 제공업체와만 통신하며, 추적을 포함하지 않고, 오픈 소스 소프트웨어로서 완전히 감사할 수 있습니다. src/app/pages/privacy/privacy-history-archive.component.html - 353,420 + 831,898 @@ -9635,8 +9655,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Last updated: September 2026 - 최종 업데이트: 2026년 9월 + Last updated: September 29, 2026 + 최종 업데이트: 2026년 9월 29일 src/app/pages/privacy/privacy-page.component.html 6,9 @@ -10563,8 +10583,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. - FSB v0.9.69에서는 프로젝트가 집계된 도입 수치를 공개할 수 있도록 수신 거부 가능한 익명 사용 통계 파이프라인을 도입했습니다(/stats 참조). 이 과정에서 사용자가 탐색하는 페이지에는 전혀 접근하지 않습니다. 유일한 위치 신호는 서버가 수집 시점에 요청 IP에서 도출하는 대략적인 국가/주 라벨이며(페이지 콘텐츠에서 도출하지 않습니다) 집계 형태로만 공개됩니다. 아래 지역(주 단위) 지표를 참조하십시오. 통계는 기본적으로 켜져 있으나 토글 하나로 끌 수 있으며, 설치별 데이터는 요청 시 삭제할 수 있습니다. + FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse place label — city, state or province, and country — that the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (city-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. + FSB v0.9.69에서는 프로젝트가 집계된 도입 수치를 공개할 수 있도록 수신 거부 가능한 익명 사용 통계 파이프라인을 도입했습니다(/stats 참조). 이 과정에서 사용자가 탐색하는 페이지에는 전혀 접근하지 않습니다. 유일한 위치 신호는 도시, 주 또는 도, 국가로 이루어진 대략적인 장소 라벨로, 서버가 수집 시점에 요청 IP에서 도출하며(페이지 콘텐츠에서 도출하지 않습니다) 집계 형태로만 공개됩니다. 아래 지역(도시 단위) 지표를 참조하십시오. 통계는 기본적으로 켜져 있으나 토글 하나로 끌 수 있으며, 설치별 데이터는 요청 시 삭제할 수 있습니다. src/app/pages/privacy/privacy-page.component.html 197,199 @@ -10651,8 +10671,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. - 평문 IP 주소. 서버는 요청 IP를 일시적으로, 그리고 인라인으로 정확히 세 가지 용도에만 사용합니다. 속도 제한을 위한 매일 교체되는 솔트 해시, 대략적인 국가/주 라벨 도출(아래 참조), 그리고 주소가 IPv4인지 IPv6인지 기록하는 것입니다. 그 후 즉시 폐기합니다. 평문 IP는 저장하거나 기록하지 않습니다. + Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse city, state, and country label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. + 평문 IP 주소. 서버는 요청 IP를 일시적으로, 그리고 인라인으로 정확히 세 가지 용도에만 사용합니다. 속도 제한을 위한 매일 교체되는 솔트 해시, 대략적인 도시/주/국가 라벨 도출(아래 참조), 그리고 주소가 IPv4인지 IPv6인지 기록하는 것입니다. 그 후 즉시 폐기합니다. 평문 IP는 저장하거나 기록하지 않습니다. src/app/pages/privacy/privacy-page.component.html 213,214 @@ -10675,43 +10695,51 @@ https://full-selfbrowsing.com/api/telemetry/forget - Region (state-level) metric - 지역(주 단위) 지표 + Region (city-level) metric + 지역(도시 단위) 지표 src/app/pages/privacy/privacy-page.component.html 218,220 - The server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. - 서버는 수집 시점에 요청 IP로부터 대략적인 국가 및 미국 주(하위 행정구역) 라벨을 도출합니다. 이때 자체 호스팅하는 DB-IP IP-to-City Lite 데이터셋과 자체 조회를 사용하며, 실시간 제3자 위치 서비스는 사용하지 않고 MaxMind도 결코 사용하지 않습니다. 평문 IP는 인라인으로 소비된 뒤 폐기되며, 도출된 라벨과 주소 체계(IPv4 또는 IPv6)만 보관됩니다. + The server derives a coarse place label — country, state or province, and city — from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. + 서버는 수집 시점에 요청 IP로부터 대략적인 장소 라벨(국가, 주 또는 도, 도시)을 도출합니다. 이때 자체 호스팅하는 DB-IP IP-to-City Lite 데이터셋과 자체 조회를 사용하며, 실시간 제3자 위치 서비스는 사용하지 않고 MaxMind도 결코 사용하지 않습니다. 평문 IP는 인라인으로 소비된 뒤 폐기되며, 도출된 라벨과 주소 체계(IPv4 또는 IPv6)만 보관됩니다. src/app/pages/privacy/privacy-page.component.html 220,221 - Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs. - 지역은 k≥5 익명성 하한을 적용한 집계 형태로만 한정하여 공개됩니다. 고유 설치 수가 5 미만인 주는 모두 하나의 "기타" 묶음으로 합쳐지며(합쳐진 수마저 5 미만이면 완전히 억제됩니다), 공개되는 주 라벨이 설치 5건 미만을 나타내는 일은 결코 없습니다. + Region is published only in aggregate, behind a k≥5 anonymity floor applied level by level: an install is counted under its city when at least 5 distinct installs share that city, otherwise under its state or province, then its country, by the same rule. Anything still below 5 collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). Each install is counted in exactly one bucket, and no published city, state, or country label ever represents fewer than 5 installs. + 지역은 단계별 k≥5 익명성 하한을 적용한 집계 형태로만 한정하여 공개됩니다. 설치는 같은 도시를 공유하는 고유 설치가 5건 이상이면 해당 도시로 집계되고, 그렇지 않으면 같은 규칙에 따라 주 또는 도, 그다음 국가로 집계됩니다. 그래도 5 미만인 항목은 모두 하나의 "기타" 묶음으로 합쳐집니다(합쳐진 수마저 5 미만이면 완전히 억제됩니다). 각 설치는 정확히 하나의 묶음에만 집계되며, 공개되는 도시·주·국가 라벨이 설치 5건 미만을 나타내는 일은 결코 없습니다. src/app/pages/privacy/privacy-page.component.html 221 - - The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). - 대략적인 라벨은 원시 이벤트(7일 보관 기간이 지나면 삭제)와 설치의 일별 집계에 저장되며, 일별 집계는 /stats가 각 설치를 가장 최근 지역 기준으로 한 번만 셀 수 있도록 365일간 보관됩니다. 이 라벨은 IP 주소, 도시, 좌표가 아닙니다. 삭제를 요청하면 설치별 나머지 데이터와 함께 삭제되며, 위의 k≥5 하한 뒤에서만 공개됩니다. 위치 데이터 제공: DB-IP (https://db-ip.com). + + The /stats globe places each published label at the approximate center of that city, state, or country, taken from the same dataset — never at the location of any install. + /stats의 지구본은 공개된 각 라벨을 같은 데이터셋에서 가져온 해당 도시, 주 또는 국가의 대략적인 중심에 표시하며, 개별 설치의 위치에는 결코 표시하지 않습니다. src/app/pages/privacy/privacy-page.component.html 222,223 + + The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, street address, or coordinates — a city name is the finest detail kept; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). + 대략적인 라벨은 원시 이벤트(7일 보관 기간이 지나면 삭제)와 설치의 일별 집계에 저장되며, 일별 집계는 /stats가 각 설치를 가장 최근 지역 기준으로 한 번만 셀 수 있도록 365일간 보관됩니다. 이 라벨은 IP 주소, 상세 주소, 좌표가 아니며, 보관되는 가장 세밀한 정보는 도시 이름입니다. 삭제를 요청하면 설치별 나머지 데이터와 함께 삭제되며, 위의 k≥5 하한 뒤에서만 공개됩니다. 위치 데이터 제공: DB-IP (https://db-ip.com). + + src/app/pages/privacy/privacy-page.component.html + 223,224 + + Retention 보관 기간 src/app/pages/privacy/privacy-page.component.html - 225,226 + 226,227 @@ -10719,7 +10747,7 @@ https://full-selfbrowsing.com/api/telemetry/forget원시 이벤트는 7일간 보관합니다. 일별 집계(설치당 하루 한 행, 대략적인 지역 라벨 포함)는 365일간 보관합니다. 전역 집계(하루 한 행, 설치별 구분 없음)는 /stats의 과거 차트가 안정적으로 유지되도록 무기한 보관합니다. src/app/pages/privacy/privacy-page.component.html - 226,228 + 227,229 @@ -10727,7 +10755,7 @@ https://full-selfbrowsing.com/api/telemetry/forget수신 거부 방법 src/app/pages/privacy/privacy-page.component.html - 228,229 + 229,230 @@ -10735,7 +10763,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB 제어판을 열고 고급 설정으로 스크롤한 뒤 Send anonymous usage data를 끄십시오. 변경 사항은 즉시 적용되며, 이후로는 해당 설치에서 어떤 이벤트도 전송되지 않습니다. src/app/pages/privacy/privacy-page.component.html - 229,231 + 230,232 @@ -10743,7 +10771,7 @@ https://full-selfbrowsing.com/api/telemetry/forget데이터 삭제 방법 src/app/pages/privacy/privacy-page.component.html - 231,232 + 232,233 @@ -10751,7 +10779,7 @@ https://full-selfbrowsing.com/api/telemetry/forget설치와 연결된 모든 통계 행의 삭제를 요청하려면(GDPR 제17조), fsbInstallUuid에서 Chrome DevTools → 애플리케이션 → 저장소 → 확장 프로그램 저장소 경로로 HTTP 요청을 한 번 보내십시오: src/app/pages/privacy/privacy-page.component.html - 232,233 + 233,234 @@ -10763,7 +10791,7 @@ https://full-selfbrowsing.com/api/telemetry/forget src/app/pages/privacy/privacy-page.component.html - 233,237 + 234,238 @@ -10771,7 +10799,7 @@ https://full-selfbrowsing.com/api/telemetry/forget제한적 사용 확약 src/app/pages/privacy/privacy-page.component.html - 237,238 + 238,239 @@ -10779,7 +10807,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB의 익명 사용 통계는 오직 full-selfbrowsing.com/stats에 공개적으로 표시되는 집계 사용 통계를 계산하는 데에만 사용됩니다. 이 데이터는 판매되거나, 제3자와 공유되거나, 광고에 사용되거나, 어떤 머신러닝 모델의 학습에 사용되지 않습니다. 이 약속은 Chrome Web Store의 Limited Use 요건을 충족합니다. src/app/pages/privacy/privacy-page.component.html - 238,240 + 239,241 @@ -10787,7 +10815,7 @@ https://full-selfbrowsing.com/api/telemetry/forget공개 집계 지표 src/app/pages/privacy/privacy-page.component.html - 240,241 + 241,242 @@ -10795,7 +10823,7 @@ https://full-selfbrowsing.com/api/telemetry/forget이 통계 파이프라인에서 도출한 집계 지표를 /stats에 공개합니다. 개수와 합계만 표시하며, 설치별 행은 결코 공개하지 않습니다. src/app/pages/privacy/privacy-page.component.html - 241,243 + 242,244 @@ -10803,7 +10831,7 @@ https://full-selfbrowsing.com/api/telemetry/forget동의 및 감사 제어 src/app/pages/privacy/privacy-page.component.html - 244,245 + 245,246 @@ -10811,7 +10839,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB의 자동화 태세는 수신 거부 방식입니다. 차단 목록에 없는 출처는 사용자가 구성할 수 있는 전역 기본값을 상속하며 현재 기본값은 자동입니다. 출처별 명시 정책으로 특정 출처를 끄기, 확인, 자동 중 하나로 설정할 수 있습니다. 감사 로그 보관은 로컬에서 제한적으로 이루어지며, 제어판에서 내보내기와 삭제를 할 수 있습니다. src/app/pages/privacy/privacy-page.component.html - 245,247 + 246,248 @@ -10819,7 +10847,7 @@ https://full-selfbrowsing.com/api/telemetry/forget서비스 차단 목록은 여전히 강제 차단입니다. 차단된 출처는 전역 기본값이나 저장된 출처별 정책과 관계없이 활성화할 수 없습니다. src/app/pages/privacy/privacy-page.component.html - 247,248 + 248,249 @@ -10827,7 +10855,7 @@ https://full-selfbrowsing.com/api/telemetry/forget민감한 출처는 자동 상태에서 읽기를 수행할 수 있으나, 쓰기는 실행 전에 출처별 변경 동의를 다시 확인합니다. 민감하지 않은 출처는 제어판의 동의 및 감사 섹션에서 수신 거부하거나 확인 모드로 변경할 수 있습니다. src/app/pages/privacy/privacy-page.component.html - 248,249 + 249,250 @@ -10835,7 +10863,7 @@ https://full-selfbrowsing.com/api/telemetry/forget모든 기능 호출은 편집된 추가 전용 로컬 감사 로그에 기록됩니다. 인수, 토큰, 쿠키, 응답 본문은 결코 저장되지 않습니다. src/app/pages/privacy/privacy-page.component.html - 249,252 + 250,253 @@ -10843,7 +10871,7 @@ https://full-selfbrowsing.com/api/telemetry/forget오픈 소스 src/app/pages/privacy/privacy-page.component.html - 253,254 + 254,255 @@ -10851,7 +10879,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB는 MIT 라이선스로 완전히 공개된 오픈 소스입니다. 모든 코드를 직접 검토하여 이러한 개인정보 보호 주장을 확인할 수 있습니다. 소스 코드는 GitHub에서 확인할 수 있습니다. src/app/pages/privacy/privacy-page.component.html - 254,256 + 255,257 @@ -10859,7 +10887,7 @@ https://full-selfbrowsing.com/api/telemetry/forget본 방침의 변경 src/app/pages/privacy/privacy-page.component.html - 257,258 + 258,259 @@ -10867,7 +10895,7 @@ https://full-selfbrowsing.com/api/telemetry/forget본 방침이 업데이트되면 이 페이지 상단의 "최종 업데이트" 날짜에 변경 사항이 반영됩니다. 중요한 변경 사항은 프로젝트의 GitHub 릴리스 노트에도 기재됩니다. src/app/pages/privacy/privacy-page.component.html - 258,260 + 259,261 @@ -10875,7 +10903,7 @@ https://full-selfbrowsing.com/api/telemetry/forget문의 src/app/pages/privacy/privacy-page.component.html - 261,262 + 262,263 @@ -10883,7 +10911,7 @@ https://full-selfbrowsing.com/api/telemetry/forget본 개인정보처리방침이나 FSB의 데이터 처리에 대해 궁금한 점이 있으면 GitHub Issues에 이슈를 등록해 주십시오. src/app/pages/privacy/privacy-page.component.html - 262,264 + 263,265 @@ -10891,7 +10919,7 @@ https://full-selfbrowsing.com/api/telemetry/forget방침 변경 이력 src/app/pages/privacy/privacy-page.component.html - 265,266 + 266,267 @@ -10899,7 +10927,7 @@ https://full-selfbrowsing.com/api/telemetry/forget아래 각 항목은 표시된 날짜 기준 개인정보처리방침의 완전한 스냅숏입니다. 어느 시점에 무엇을 약속했는지 확인할 수 있도록 이전 버전을 보관합니다. src/app/pages/privacy/privacy-page.component.html - 266,268 + 267,269 @@ -11623,7 +11651,7 @@ https://full-selfbrowsing.com/api/telemetry/forget브라우저 조작 중 src/app/pages/release-notes/release-notes-page.component.html - 176,180 + 176,179 @@ -11775,7 +11803,7 @@ https://full-selfbrowsing.com/api/telemetry/forget입력 불가 · 다른 자동화가 이 탭을 소유합니다 src/app/pages/release-notes/release-notes-page.component.html - 226,231 + 226,230 @@ -12351,7 +12379,7 @@ https://full-selfbrowsing.com/api/telemetry/forget요소 src/app/pages/release-notes/release-notes-page.component.html - 408,412 + 408,411 @@ -12511,7 +12539,7 @@ https://full-selfbrowsing.com/api/telemetry/forget560개 보호됨 src/app/pages/release-notes/release-notes-page.component.html - 469,473 + 469,472 @@ -12671,7 +12699,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetnode 24 src/app/pages/release-notes/release-notes-page.component.html - 529,533 + 529,532 @@ -17999,7 +18027,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB - 릴리스 노트 src/app/pages/release-notes/release-notes-page.component.ts - 88 + 86 @@ -18007,7 +18035,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB의 모든 릴리스를 그림과 함께 소개합니다. 위임된 에이전트 런타임, 도구 모음 활동 아이콘, 다중 에이전트 탭 소유권, MCP 브리지, 기능 카탈로그, 그리고 최초의 Chrome 확장 프로그램 시제품까지. src/app/pages/release-notes/release-notes-page.component.ts - 89 + 87 @@ -18179,11 +18207,11 @@ https://full-selfbrowsing.com/api/telemetry/forget - Where installs were last seen, by coarse region, over the past 365 days. - 지난 365일 동안 설치가 마지막으로 확인된 위치를 대략적인 지역별로 보여 줍니다. + Where installs were last seen over the past 365 days, by city, state, or country. Place data by DB-IP. + 지난 365일 동안 설치가 마지막으로 확인된 위치를 도시, 주 또는 국가별로 보여 줍니다. 위치 데이터 제공: DB-IP. src/app/pages/stats/stats-page.component.html - 104,106 + 104,105 @@ -18239,7 +18267,7 @@ https://full-selfbrowsing.com/api/telemetry/forget네트워크 또는 구문 분석 오류입니다. src/app/pages/stats/stats-page.component.ts - 132 + 133 @@ -18247,7 +18275,7 @@ https://full-selfbrowsing.com/api/telemetry/forget스타 src/app/pages/stats/stats-page.component.ts - 139 + 140 @@ -18255,7 +18283,7 @@ https://full-selfbrowsing.com/api/telemetry/forget커밋 src/app/pages/stats/stats-page.component.ts - 140 + 141 @@ -18263,7 +18291,7 @@ https://full-selfbrowsing.com/api/telemetry/forget현재 활성 src/app/pages/stats/stats-page.component.ts - 141 + 142 @@ -18271,7 +18299,7 @@ https://full-selfbrowsing.com/api/telemetry/forget토큰 src/app/pages/stats/stats-page.component.ts - 142 + 143 @@ -18279,27 +18307,7 @@ https://full-selfbrowsing.com/api/telemetry/forget인기 src/app/pages/stats/stats-page.component.ts - 143 - - - - Could not load chart library. - 차트 라이브러리를 불러오지 못했습니다. - - src/app/pages/stats/stats-page.component.ts - 183 - - - - Could not render the selected chart. - 선택한 차트를 렌더링하지 못했습니다. - - src/app/pages/stats/stats-page.component.ts - 190 - - - src/app/pages/stats/stats-page.component.ts - 862 + 144 @@ -18307,7 +18315,7 @@ https://full-selfbrowsing.com/api/telemetry/forget통계를 준비하는 중입니다. 잠시 후 다시 시도합니다. src/app/pages/stats/stats-page.component.ts - 232 + 233 @@ -18315,7 +18323,7 @@ https://full-selfbrowsing.com/api/telemetry/forget통계 응답에 최신성 메타데이터가 없습니다. src/app/pages/stats/stats-page.component.ts - 235 + 236 @@ -18323,7 +18331,7 @@ https://full-selfbrowsing.com/api/telemetry/forget사용 가능한 마지막 스냅숏이 24시간을 넘었습니다. src/app/pages/stats/stats-page.component.ts - 238 + 239 @@ -18331,7 +18339,7 @@ https://full-selfbrowsing.com/api/telemetry/forget통계 응답의 형식이 올바르지 않습니다. src/app/pages/stats/stats-page.component.ts - 241 + 242 @@ -18339,7 +18347,7 @@ https://full-selfbrowsing.com/api/telemetry/forget통계는 브라우저에서만 이용할 수 있습니다. src/app/pages/stats/stats-page.component.ts - 244 + 245 @@ -18347,7 +18355,7 @@ https://full-selfbrowsing.com/api/telemetry/forget시간에 따른 저장소 누적 스타 src/app/pages/stats/stats-page.component.ts - 252 + 253 @@ -18355,7 +18363,7 @@ https://full-selfbrowsing.com/api/telemetry/forget시간에 따른 저장소 누적 커밋 src/app/pages/stats/stats-page.component.ts - 254 + 255 @@ -18363,7 +18371,7 @@ https://full-selfbrowsing.com/api/telemetry/forget최근 30일간 FSB 토큰 사용량 src/app/pages/stats/stats-page.component.ts - 256 + 257 @@ -18371,7 +18379,7 @@ https://full-selfbrowsing.com/api/telemetry/forget추적 중인 MCP 클라이언트 점유율 src/app/pages/stats/stats-page.component.ts - 258 + 259 @@ -18379,7 +18387,7 @@ https://full-selfbrowsing.com/api/telemetry/forget알 수 없음 src/app/pages/stats/stats-page.component.ts - 295 + 296 @@ -18387,15 +18395,15 @@ https://full-selfbrowsing.com/api/telemetry/forget기타 src/app/pages/stats/stats-page.component.ts - 296 + 297 - Globe showing where FSB installs were last seen over the past 365 days, by coarse region - 지난 365일 동안 FSB 설치가 마지막으로 확인된 위치를 대략적인 지역별로 보여 주는 지구본 + Globe showing where FSB installs were last seen over the past 365 days, by city, state, or country + 지난 365일 동안 FSB 설치가 마지막으로 확인된 위치를 도시, 주 또는 국가별로 보여 주는 지구본 src/app/pages/stats/stats-page.component.ts - 308 + 309 @@ -18403,7 +18411,7 @@ https://full-selfbrowsing.com/api/telemetry/forget전체 스타 src/app/pages/stats/stats-page.component.ts - 317 + 318 @@ -18411,7 +18419,7 @@ https://full-selfbrowsing.com/api/telemetry/forget최근 7일 src/app/pages/stats/stats-page.component.ts - 318 + 319 @@ -18419,7 +18427,7 @@ https://full-selfbrowsing.com/api/telemetry/forget전체 커밋 src/app/pages/stats/stats-page.component.ts - 325 + 326 @@ -18427,7 +18435,7 @@ https://full-selfbrowsing.com/api/telemetry/forget최근 30일 src/app/pages/stats/stats-page.component.ts - 329 + 330 @@ -18435,7 +18443,7 @@ https://full-selfbrowsing.com/api/telemetry/forget활성 에이전트 src/app/pages/stats/stats-page.component.ts - 343 + 344 @@ -18443,7 +18451,7 @@ https://full-selfbrowsing.com/api/telemetry/forget보고 사용자당 평균 src/app/pages/stats/stats-page.component.ts - 347 + 348 @@ -18451,7 +18459,7 @@ https://full-selfbrowsing.com/api/telemetry/forget토큰 src/app/pages/stats/stats-page.component.ts - 357 + 358 @@ -18459,7 +18467,7 @@ https://full-selfbrowsing.com/api/telemetry/forget토큰 (24시간) src/app/pages/stats/stats-page.component.ts - 358 + 359 @@ -18467,7 +18475,7 @@ https://full-selfbrowsing.com/api/telemetry/forget추적 중인 클라이언트 src/app/pages/stats/stats-page.component.ts - 364 + 365 @@ -18475,7 +18483,7 @@ https://full-selfbrowsing.com/api/telemetry/forget상위: src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18483,7 +18491,7 @@ https://full-selfbrowsing.com/api/telemetry/forget상위 클라이언트 src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18491,7 +18499,7 @@ https://full-selfbrowsing.com/api/telemetry/forget통계 보기를 선택하십시오. 현재 보기: src/app/pages/stats/stats-page.component.ts - 402 + 403 @@ -18499,7 +18507,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB · 통계 src/app/pages/stats/stats-page.component.ts - 478 + 484 @@ -18507,7 +18515,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB의 실시간 도입, 사용량, 저장소 신호 집계입니다. src/app/pages/stats/stats-page.component.ts - 486 + 492 @@ -18515,7 +18523,7 @@ https://full-selfbrowsing.com/api/telemetry/forget누적 스타 src/app/pages/stats/stats-page.component.ts - 974 + 979 @@ -18523,7 +18531,7 @@ https://full-selfbrowsing.com/api/telemetry/forget누적 커밋 src/app/pages/stats/stats-page.component.ts - 995 + 1000 @@ -18531,7 +18539,7 @@ https://full-selfbrowsing.com/api/telemetry/forget토큰 (최근 30일) src/app/pages/stats/stats-page.component.ts - 1023 + 1028 @@ -18539,7 +18547,23 @@ https://full-selfbrowsing.com/api/telemetry/forget인기 MCP 클라이언트 src/app/pages/stats/stats-page.component.ts - 1046 + 1051 + + + + Could not load chart library. + 차트 라이브러리를 불러오지 못했습니다. + + src/app/pages/stats/stats-page.component.ts + 1105 + + + + Could not render the selected chart. + 선택한 차트를 렌더링하지 못했습니다. + + src/app/pages/stats/stats-page.component.ts + 1106 diff --git a/showcase/angular/src/locale/messages.xlf b/showcase/angular/src/locale/messages.xlf index 1218e9a7..4cfaf45c 100644 --- a/showcase/angular/src/locale/messages.xlf +++ b/showcase/angular/src/locale/messages.xlf @@ -8357,11 +8357,27 @@ 307 + + September 2026v0.9.91 — 365-day region retention, Region (state-level) metric (full archived text) + + src/app/pages/privacy/privacy-history-archive.component.html + 3,6 + + + + Archived copy of the privacy policy as it stood from September 28, 2026, before the September 29, 2026 update.TLDR: FSB runs inside your browser, and no browsing data is collected by FSB servers. AI calls go directly from your browser to the provider you choose. API keys and credentials are encrypted locally, and memory data stays on your device. If you install the optional local agent service, FSB can also be driven by an MCP client running on your own machine over a loopback connection, and by coding-agent CLIs you have already installed. The only data that ever leaves your machine for FSB is opt-out anonymous usage telemetry that powers the public /stats page. If you pair Remote Dashboard, transient live-preview frames can pass through the relay during the session, but they are not stored. Everything is open source and auditable.Data CollectionFSB operates entirely within your browser. When you initiate an automation task, the extension may inspect the active tab's DOM (Document Object Model) and, when you invoke a capability, make same-origin site API requests from your browser session.No browsing history is collected or stored beyond the current sessionDOM data and site API results are analyzed locally and discarded after each automation step unless you explicitly save them in memoryNo personal information is harvested from pages you visitSite API capabilitiesFSB's capability layer can call a site's own first-party API from the page context, using your already-authenticated browser session. These requests run locally in Chrome; first-party cookies or site auth may be attached by the browser for that target site, but FSB does not return, store, log, or send cookies, tokens, CSRF values, request bodies, or response bodies to FSB servers.Capability invocations follow FSB's consent controls and are recorded only in the redacted local audit log: origin, capability slug, method, side-effect class, consent decision, and outcome. The audit log never stores invocation arguments, request bodies, response bodies, cookies, tokens, CSRF values, or site response payloads.Chrome permissions FSB requestsTo run web automation, FSB declares the following permissions in its Chrome manifest. Each is used only for the documented purpose; nothing is sent off-device on the strength of any of them.DOM and tabs, activeTab, scripting, tabs, windows, sidePanel, and host permission <all_urls>: read and write the active tab, inject the automation content script, list and switch tabs, and render the side panelAdvanced automation, debugger: attach the Chrome DevTools Protocol for coordinate-based clicks, drag, and key-hold actions that the regular DOM API cannot perform. webNavigation: observe navigation start/finish events so automation waits for the right momentLocal storage, storage, unlimitedStorage: store your settings, credentials, payment methods, and memory in chrome.storage.local on your device. Unlimited storage lifts the default 10 MB quota so memory and session logs can grow without hitting a wallUX helpers, clipboardWrite: write copy-to-clipboard results from automation. alarms: schedule background housekeeping. offscreen: host the speech-to-text recorder in a hidden document because service workers cannot capture audio directlyLocal service, nativeMessaging: start the optional local agent service described under Local Agent Service and Native Messaging below. Nothing about the pages you visit crosses that channel. system.memory: read the total installed RAM figure only, so FSB can suggest a sensible limit on how many agents run at once. The available-memory and per-processor breakdowns are never read, and the figure never leaves the extensionMicrophone access for speech-to-text is not declared in the manifest. Chrome shows its own permission prompt the first time you use the mic button.Data StorageAll settings and data are stored locally in Chrome's extension storage, with sensitive values encrypted at rest as described in the sections below.Configuration is stored in chrome.storage.localAPI keys, credentials, and payment methods are encrypted before storage, each detailed in its own section belowSession logs are stored locally and can be cleared at any timeAnalytics data (task counts, success rates) stays on your deviceExternal ServicesFSB communicates with external AI providers only when you configure and use a hosted provider. If you use LM Studio, AI requests stay on your machine through its local OpenAI-compatible server. The choice of provider and what data is sent is under your control.Hosted API calls are made only to the provider you select (xAI, OpenAI, Anthropic, Google, or OpenRouter)LM Studio uses a local OpenAI-compatible server on your device and does not require an API keySent data includes: task description, DOM structure summary, and action contextIf you pair Remote Dashboard or use legacy Background Agents sync, an optional relay server handles WebSocket messages for that session. Live-preview frames may pass through the relay transiently, but none of the following is ever persisted on an FSB server: page content, DOM data, browsing history, screenshots, AI prompts, AI responses, cookies, tokens, or site API payloads. This is opt-in only, and the rest of this policy refers back to this list rather than restating itEach provider has their own privacy policy governing how they handle API requestsLocal Agent Service and Native MessagingFSB can be driven by an MCP client running on your own machine. That requires a small local agent service which you install yourself, and the nativeMessaging permission exists solely so the extension can start it. Chrome cannot launch a local program directly, so this is the only supported mechanism.The messaging host io.github.fullselfbrowsing.fsb_native_host is registered only by an explicit action you take, by running fsb-mcp-server install --native-host. The extension cannot install it, and its registration names one permitted extension originThe exchange is a closed schema. FSB sends a version number, an action of either wake or bootstrap, and a locally generated correlation identifier; it receives that identifier back with an outcome and a reason drawn from a fixed list. Any additional field is rejected outright and messages are capped at 4 KB. No URL, tab, page content, or DOM data exists anywhere in this schemaA successful start returns a pairing code. This is a loopback authentication secret generated on your device and stored with owner-only permissions, rotated every time the service starts. It carries no information about you or your browsingThe service listens on your machine only. The extension bridge refuses to bind any address that is not loopback, and the MCP endpoint binds 127.0.0.1 by default and whenever FSB starts itUnder its own directory the service keeps only operational records: a diagnostics log of delegated-run outcomes restricted to a fixed field list, and a process-supervision journal. Task text, environment values, binary paths, and error text never reach either of them, and entries carrying credential-shaped values are discarded rather than writtenIf the host is not installed, the feature simply does not start. Nothing is transmitted, and the rest of the extension continues to work normallyDelegated CLI AgentsFSB can hand a task to a coding-agent command-line tool you have already installed, currently Claude Code or Grok Build. FSB starts that program on your own machine; your task is never proxied through FSB servers.Your task text reaches the tool over its standard input and nowhere else. FSB checks this before every run: if the task appears in the command line, the working directory, the environment, or any file path, the run is aborted rather than started. Tasks are capped at 64 KBWhile the run is in progress the tool calls back into FSB over the loopback connection to drive your browser. Whatever it reads that way — page text, DOM snapshots, spreadsheet values, screenshots — is returned to that tool, and therefore reaches its vendor: Anthropic for Claude Code, xAI for Grok BuildThe run uses the account already signed in to that tool. FSB does not hold, read, or transmit your Anthropic or xAI credential, and any usage or charges follow that vendor's own planGrok Build runs inside a profile directory FSB keeps separate from your own. The sign-in credential stored there is written by the Grok tool itself during its own sign-in flowThe agent instructions FSB supplies state that passwords, CVV values, payment-card data, and saved credentials must never enter prompts, narration, logs, or tool argumentsReading and Filling Google SheetsFSB can read and fill a range in Google Sheets by driving the spreadsheet interface in your own already-authenticated browser session.Reading a range returns those cell values to the model you selected, exactly like any other page content FSB reads on your behalf. Only the range you asked for is read; there is no whole-spreadsheet exportSpreadsheet content is stripped out before anything is written to FSB's local session history. Only shape counts survive — how many rows, columns, and values were involved. The spreadsheet address, sheet name, cell references, and every cell value are discardedIf that filtering cannot be applied to a record for any reason, the record is dropped rather than storedRemote Dashboard and PhantomStream Live PreviewWhen you pair Remote Dashboard, FSB can show a live preview of the active browser tab using PhantomStream. The preview streams structured DOM data instead of pixels: an initial snapshot, MutationObserver diffs, scroll position, automation overlays, dialog state, media playback state, and remote-control messages over WebSocket.The relay forwards those live-preview frames only to the paired dashboard session, and nothing from the preview stream is persisted on FSB servers — see the list under External Services aboveFSB enables PhantomStream input masking (maskInputs: true), so passwords and form-control values are masked before they leave the captured pageThe dashboard viewer renders mirrored content in a scriptless sandbox and sanitizes mirrored DOM and CSS before displayImages, video, and audio are mirrored by reference in current dashboard mode. Media bytes do not cross the relay; the viewer may fetch allowed public HTTPS asset or media URLs directly, while private, internal, non-HTTPS, or otherwise blocked origins are replaced with placeholders by PhantomStream's fail-closed fetch policyNo Third-Party TrackingFSB does not include any third-party analytics, ad trackers, or cross-site fingerprinting. There are no cookies and no third-party scripts beyond the AI provider APIs you explicitly configure. The one piece of first-party data FSB sends home is the opt-out Anonymous Usage Telemetry described below, used solely to power the public /stats dashboard.API KeysYour API keys are encrypted locally using AES-GCM before being stored. They are never transmitted anywhere except to the AI provider you configured, and only as authentication headers in API requests.Keys are encrypted at rest in Chrome storageDecryption only happens in-memory when making API callsKeys are never logged, exported, or sharedAuto-PasswordsFSB includes an optional credential manager that stores login credentials encrypted on your device. Passwords are never exposed to AI models. They are filled directly into pages by the content script, bypassing the AI entirely.Credentials are encrypted at rest using AES-GCM with 256-bit keys and PBKDF2 key derivationWhen the AI analyzes a page, password field values are replaced with [hidden]. The actual password is never included in any AI promptAuto-fill is performed by the content script injecting values directly into the DOM, with no AI involvement in the credential flowThe credential list view only shows usernames and domains. Passwords are decrypted individually and only when needed for auto-fillCredentials are stored per-domain with parent domain fallback (e.g., accounts.google.com inherits from google.com)Payment MethodsFSB includes an optional payment-method vault that stores card details on your device for checkout auto-fill. Cards are treated with the same encryption and AI isolation as login credentials, and the full card number is never sent to any AI model.Card details (number, expiry, cardholder, and zip) are encrypted at rest using AES-GCM with the same vault-derived key used for credentialsWhen the AI analyzes a checkout page, any detected card-number field values are replaced with [hidden] before the prompt is built. Card numbers, CVV, and expiry are never included in any AI promptAuto-fill happens via the content script writing directly into the page's DOM fields, bypassing the AI entirelyThe list view shows only a card nickname and last-4 digits. Full numbers are decrypted in memory only at the moment of fillAn MCP client can request a payment fill via use_payment_method, but the user is shown an in-extension confirmation prompt before any card data is written into the pageCVV is never persisted unless you opt in per-card, and even then it is encrypted alongside the rest of the recordSpeech-to-TextFSB includes an optional microphone input for the prompt box. The default provider runs entirely in your browser; an optional OpenAI Whisper fallback can be enabled in settings if you want higher accuracy.Default provider: the browser's native SpeechRecognition API. Audio is processed by Chrome and never leaves your device through FSBOptional Whisper provider: when sttProvider is set to whisper and an OpenAI key is configured, recorded audio chunks are uploaded directly from your browser to OpenAI's transcription endpoint. FSB never sees or stores the audioThe microphone is only active while you are holding or have toggled the mic button. Chrome prompts for permission the first time you use it; FSB does not request microphone access in the extension manifestTranscripts are inserted into the prompt textarea only and are never logged, persisted, or transmitted outside the active AI request you choose to sendDisable speech entirely by leaving the mic button untouched, or by clearing the optional Whisper provider in Chrome extension storagePrompt Injection PreventionWeb pages can contain hidden text designed to hijack AI agents. FSB implements multi-layered defenses to ensure the AI only follows your instructions, never instructions embedded in page content.All page content is wrapped in [PAGE_CONTENT] boundary markers, and the AI is instructed to never follow instructions found within these markersA sanitization engine strips known injection patterns (e.g., "ignore previous instructions", fake system prompts, override attempts) from all page content before it reaches the AIAI-generated actions are validated before execution. Dangerous URLs (javascript:, data:) and script injection attempts are blockedOnly a strict, fixed allowlist of known tools can be executed. The AI cannot invent or call arbitrary actionsContent size is capped (500 chars per value, 15K total prompt cap) to limit payload deliveryInvisible Unicode control characters that websites embed are stripped before processingBackground Agents and Server SyncDeprecated in v0.9.45rc1. FSB's built-in Background Agents have been superseded by OpenClaw and Claude Routines, with remote control now handled by the Sync tab. The disclosures below are retained for users still running v0.9.44 or earlier; on current builds the relay server is only contacted when you pair a Sync session.If you opt into legacy Background Agents server sync or pair Remote Dashboard, a relay server facilitates communication between your extension and the dashboard.The server stores: agent definitions (name, schedule, target URL), run metrics (token count, cost, duration, success/fail status), and session pairing tokensBeyond that run metadata the server persists nothing derived from the pages you visit — see the list under External Services aboveAuthentication uses hash keys (generated locally) and session tokens that expire after 24 hoursOne-time pairing tokens expire after 60 seconds and cannot be reusedServer sync is disabled by default. You must explicitly enable it in OptionsMemory SystemFSB's memory system stores navigation patterns and site intelligence to improve automation over time.All memory data (semantic, episodic, procedural) is stored locally in chrome.storage.localNo memory data is sent to any external serverMemory can be viewed and cleared at any time from the Options dashboardSite maps and navigation patterns are domain-specific and isolated from each otherSession Replay and ScreenshotsWhen an MCP client drives FSB, the extension records what the agent did so you can review or replay the run afterwards. These records stay on your device.Runs still in progress are held in Chrome's session storage so they survive the service worker being evicted; finished runs are written to chrome.storage.local through the same history store your own automation runs useRecords are redacted before they are stored. Values under credential-shaped keys, text typed into fields that look like password, one-time-code, or card inputs, and signed or token-bearing URL parameters are all replaced. Ordinary addresses and selectors are preserved, because replay does not work without themRecorded runs are kept for 30 days by default, adjustable between 1 and 365 days, and older ones are pruned by a daily job. Recording can be switched off entirely in Advanced SettingsScreenshot images are never written into that history. Only capture metadata such as dimensions and byte size is retainedScreenshots an MCP client requests are saved by the local service as files on your own disk with owner-only permissions and deleted automatically after seven days. The image is also handed back to the requesting client, and therefore to that client's modelAnonymous Usage TelemetryFSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.What we collectA random per-install UUID stored in chrome.storage.local under the key fsbInstallUuid. The UUID is generated locally and never tied to your identity.The name of the MCP client used (e.g. Claude Code, Cursor, Codex), drawn from a fixed allowlist.The model name used for a session (e.g. grok-4-fast, claude-opus-4), drawn from a fixed allowlist.Aggregate input/output token counts per session.The number of active FSB agents on your install (an integer count).What we do NOT collectPage URLs, hostnames, or browsing history.Prompts, instructions, task descriptions, or any natural-language text you send to your model provider.Page DOM, screenshots, page content, site API payloads, or AI responses.Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.Names, usernames, account handles, or any free-form identity fields.Email addresses, phone numbers, or contact information.Region (state-level) metricThe server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs.The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).RetentionRaw events are retained for 7 days. Daily rollups (one row per install per day, including the coarse region label) are retained for 365 days. Global aggregates (one row per day, no per-install dimension) are retained indefinitely so historical charts on /stats remain stable.How to opt outOpen the FSB Control Panel, scroll to Advanced Settings, and toggle Send anonymous usage data off. The change takes effect immediately; no further events will be sent from your install.How to erase your dataTo request erasure of all telemetry rows associated with your install (GDPR Article 17), look up your fsbInstallUuid in Chrome DevTools → Application → Storage → Extension storage, then send a single HTTP request:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Consent and Audit ControlsFSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. + + src/app/pages/privacy/privacy-history-archive.component.html + 7,238 + + August 2026v0.9.91 — Local Agent Service and Native Messaging, Delegated CLI Agents, Google Sheets, Session Replay and Screenshots (full archived text) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 242,245 @@ -8370,14 +8386,14 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Consent and Audit ControlsFSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. src/app/pages/privacy/privacy-history-archive.component.html - 7,238 + 246,477 May – July 2026v0.9.90 — Site API capabilities, Remote Dashboard and PhantomStream, Payment Methods, Speech-to-Text, Region metric (full archived text) src/app/pages/privacy/privacy-history-archive.component.html - 242,245 + 481,484 @@ -8386,14 +8402,14 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Consent and Audit ControlsFSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. src/app/pages/privacy/privacy-history-archive.component.html - 246,437 + 485,676 March 2026v9.0.2 — Background Agents, Memory System, Server Sync, Anonymous Usage Telemetry, Legal Posture (full archived text) src/app/pages/privacy/privacy-history-archive.component.html - 441,444 + 680,683 @@ -8402,21 +8418,21 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Legal Posture and Consent ModelFSB's automation posture, audit-log retention, and per-origin consent model were presented as explicit product controls. FSB does nothing on an origin until you explicitly allow it, automated read access never implies write access, and a conservative service denylist blocks automation on sensitive categories (financial and government services) outright.Per-origin consent is default-off. Auto and write (mutating) access are separate, explicit opt-ins managed from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.A service denylist renders sensitive origins non-enableable, enforced at the capability gate and not merely in the interface.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. src/app/pages/privacy/privacy-history-archive.component.html - 445,584 + 684,823 February 2026v0.9 — Initial privacy policy (full archived text) src/app/pages/privacy/privacy-history-archive.component.html - 588,591 + 827,830 Archived copy of the initial privacy policy as it stood in February 2026. This snapshot is reproduced from the original static showcase page, with the page chrome removed and the policy content preserved.Data CollectionFSB operates entirely within your browser. The extension only accesses the DOM (Document Object Model) of the currently active tab when you initiate an automation task.No browsing history is collected or stored beyond the current sessionDOM data is analyzed locally and discarded after each automation stepNo personal information is harvested from pages you visitData StorageAll settings and data are stored locally in Chrome's extension storage. FSB uses AES-GCM encryption for sensitive data like API keys.Configuration is stored in chrome.storage.localAPI keys are encrypted before storage using AES-GCMSession logs are stored locally and can be cleared at any timeAnalytics data (task counts, success rates) stays on your deviceExternal ServicesFSB communicates with external AI providers only when you configure and use them. The choice of provider and what data is sent is under your control.API calls are made only to the provider you select (xAI, OpenAI, Anthropic, or Google)Sent data includes: task description, DOM structure summary, and action contextNo data is sent to FSB servers -- there are noneEach provider has their own privacy policy governing how they handle API requestsNo TrackingFSB does not include any analytics, telemetry, or tracking services. There are no cookies, no fingerprinting, and no third-party scripts beyond the AI provider APIs you explicitly configure.API KeysYour API keys are encrypted locally using AES-GCM before being stored. They are never transmitted anywhere except to the AI provider you configured, and only as authentication headers in API requests.Keys are encrypted at rest in Chrome storageDecryption only happens in-memory when making API callsKeys are never logged, exported, or sharedAuto-Passwords BetaFSB includes an optional credential manager that stores login credentials encrypted on your device. Passwords are never exposed to AI models -- they are filled directly into pages by the content script, bypassing the AI entirely.Credentials are encrypted at rest using AES-GCM with 256-bit keys and PBKDF2 key derivationWhen the AI analyzes a page, password field values are replaced with [hidden] -- the actual password is never included in any AI promptAuto-fill is performed by the content script injecting values directly into the DOM, with no AI involvement in the credential flowThe credential list view only shows usernames and domains -- passwords are decrypted individually and only when needed for auto-fillCredentials are stored per-domain with parent domain fallback (e.g., accounts.google.com inherits from google.com)Prompt Injection PreventionWeb pages can contain hidden text designed to hijack AI agents. FSB implements multi-layered defenses to ensure the AI only follows your instructions, never instructions embedded in page content.All page content is wrapped in [PAGE_CONTENT] boundary markers, and the AI is instructed to never follow instructions found within these markersA sanitization engine strips known injection patterns (e.g., "ignore previous instructions", fake system prompts, override attempts) from all page content before it reaches the AIAI-generated actions are validated before execution -- dangerous URLs (javascript:, data:) and script injection attempts are blockedOnly a strict allowlist of 30+ known tools can be executed -- the AI cannot invent or call arbitrary actionsContent size is capped (500 chars per value, 15K total prompt cap) to limit payload deliveryInvisible Unicode control characters that websites embed are stripped before processingOpen SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.View the source code on GitHubSummaryFSB processes data locally, encrypts sensitive information, never exposes passwords to AI models, defends against prompt injection attacks, communicates only with AI providers you choose, includes no tracking, and is fully auditable as open source software. src/app/pages/privacy/privacy-history-archive.component.html - 592,659 + 831,898 @@ -8434,7 +8450,7 @@ https://full-selfbrowsing.com/api/telemetry/forget - Last updated: September 2026 + Last updated: September 29, 2026 src/app/pages/privacy/privacy-page.component.html 6,9 @@ -9246,7 +9262,7 @@ https://full-selfbrowsing.com/api/telemetry/forget - FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. + FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse place label — city, state or province, and country — that the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (city-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. src/app/pages/privacy/privacy-page.component.html 197,199 @@ -9323,7 +9339,7 @@ https://full-selfbrowsing.com/api/telemetry/forget - Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. + Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse city, state, and country label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. src/app/pages/privacy/privacy-page.component.html 213,214 @@ -9344,73 +9360,80 @@ https://full-selfbrowsing.com/api/telemetry/forget - Region (state-level) metric + Region (city-level) metric src/app/pages/privacy/privacy-page.component.html 218,220 - The server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. + The server derives a coarse place label — country, state or province, and city — from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. src/app/pages/privacy/privacy-page.component.html 220,221 - Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs. + Region is published only in aggregate, behind a k≥5 anonymity floor applied level by level: an install is counted under its city when at least 5 distinct installs share that city, otherwise under its state or province, then its country, by the same rule. Anything still below 5 collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). Each install is counted in exactly one bucket, and no published city, state, or country label ever represents fewer than 5 installs. src/app/pages/privacy/privacy-page.component.html 221 - - The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). + + The /stats globe places each published label at the approximate center of that city, state, or country, taken from the same dataset — never at the location of any install. src/app/pages/privacy/privacy-page.component.html 222,223 + + The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, street address, or coordinates — a city name is the finest detail kept; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). + + src/app/pages/privacy/privacy-page.component.html + 223,224 + + Retention src/app/pages/privacy/privacy-page.component.html - 225,226 + 226,227 Raw events are retained for 7 days. Daily rollups (one row per install per day, including the coarse region label) are retained for 365 days. Global aggregates (one row per day, no per-install dimension) are retained indefinitely so historical charts on /stats remain stable. src/app/pages/privacy/privacy-page.component.html - 226,228 + 227,229 How to opt out src/app/pages/privacy/privacy-page.component.html - 228,229 + 229,230 Open the FSB Control Panel, scroll to Advanced Settings, and toggle Send anonymous usage data off. The change takes effect immediately; no further events will be sent from your install. src/app/pages/privacy/privacy-page.component.html - 229,231 + 230,232 How to erase your data src/app/pages/privacy/privacy-page.component.html - 231,232 + 232,233 To request erasure of all telemetry rows associated with your install (GDPR Article 17), look up your fsbInstallUuid in Chrome DevTools → Application → Storage → Extension storage, then send a single HTTP request: src/app/pages/privacy/privacy-page.component.html - 232,233 + 233,234 @@ -9419,126 +9442,126 @@ https://full-selfbrowsing.com/api/telemetry/forget src/app/pages/privacy/privacy-page.component.html - 233,237 + 234,238 Limited Use affirmation src/app/pages/privacy/privacy-page.component.html - 237,238 + 238,239 FSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement. src/app/pages/privacy/privacy-page.component.html - 238,240 + 239,241 Aggregated public metrics src/app/pages/privacy/privacy-page.component.html - 240,241 + 241,242 We publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed. src/app/pages/privacy/privacy-page.component.html - 241,243 + 242,244 Consent and Audit Controls src/app/pages/privacy/privacy-page.component.html - 244,245 + 245,246 FSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel. src/app/pages/privacy/privacy-page.component.html - 245,247 + 246,248 The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy. src/app/pages/privacy/privacy-page.component.html - 247,248 + 248,249 Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section. src/app/pages/privacy/privacy-page.component.html - 248,249 + 249,250 Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored. src/app/pages/privacy/privacy-page.component.html - 249,252 + 250,253 Open Source src/app/pages/privacy/privacy-page.component.html - 253,254 + 254,255 FSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub. src/app/pages/privacy/privacy-page.component.html - 254,256 + 255,257 Changes to This Policy src/app/pages/privacy/privacy-page.component.html - 257,258 + 258,259 If this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes. src/app/pages/privacy/privacy-page.component.html - 258,260 + 259,261 Contact src/app/pages/privacy/privacy-page.component.html - 261,262 + 262,263 If you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. src/app/pages/privacy/privacy-page.component.html - 262,264 + 263,265 Policy History src/app/pages/privacy/privacy-page.component.html - 265,266 + 266,267 Each entry below is a complete snapshot of the privacy policy as it stood on the date shown. Older versions are kept so you can audit what we promised at any point in time. src/app/pages/privacy/privacy-page.component.html - 266,268 + 267,269 @@ -15909,10 +15932,10 @@ https://full-selfbrowsing.com/api/telemetry/forget - Where installs were last seen, by coarse region, over the past 365 days. + Where installs were last seen over the past 365 days, by city, state, or country. Place data by DB-IP. src/app/pages/stats/stats-page.component.html - 104,106 + 104,105 @@ -15961,266 +15984,266 @@ https://full-selfbrowsing.com/api/telemetry/forget src/app/pages/stats/stats-page.component.ts - 132 + 133 Stars src/app/pages/stats/stats-page.component.ts - 139 + 140 Commits src/app/pages/stats/stats-page.component.ts - 140 + 141 Active now src/app/pages/stats/stats-page.component.ts - 141 + 142 Tokens src/app/pages/stats/stats-page.component.ts - 142 + 143 Popular src/app/pages/stats/stats-page.component.ts - 143 + 144 Stats are warming up; retrying shortly. src/app/pages/stats/stats-page.component.ts - 232 + 233 The stats response is missing freshness metadata. src/app/pages/stats/stats-page.component.ts - 235 + 236 The last usable snapshot is more than 24 hours old. src/app/pages/stats/stats-page.component.ts - 238 + 239 The stats response was malformed. src/app/pages/stats/stats-page.component.ts - 241 + 242 Stats are only available in the browser. src/app/pages/stats/stats-page.component.ts - 244 + 245 Cumulative repository stars over time src/app/pages/stats/stats-page.component.ts - 252 + 253 Cumulative repository commits over time src/app/pages/stats/stats-page.component.ts - 254 + 255 FSB token usage over the last 30 days src/app/pages/stats/stats-page.component.ts - 256 + 257 Share of tracked MCP clients src/app/pages/stats/stats-page.component.ts - 258 + 259 Unknown src/app/pages/stats/stats-page.component.ts - 295 + 296 Other src/app/pages/stats/stats-page.component.ts - 296 + 297 - Globe showing where FSB installs were last seen over the past 365 days, by coarse region + Globe showing where FSB installs were last seen over the past 365 days, by city, state, or country src/app/pages/stats/stats-page.component.ts - 308 + 309 total stars src/app/pages/stats/stats-page.component.ts - 317 + 318 last 7 days src/app/pages/stats/stats-page.component.ts - 318 + 319 total commits src/app/pages/stats/stats-page.component.ts - 325 + 326 last 30 days src/app/pages/stats/stats-page.component.ts - 329 + 330 active agents src/app/pages/stats/stats-page.component.ts - 343 + 344 avg/reporting user src/app/pages/stats/stats-page.component.ts - 347 + 348 tokens src/app/pages/stats/stats-page.component.ts - 357 + 358 tokens (24h) src/app/pages/stats/stats-page.component.ts - 358 + 359 tracked clients src/app/pages/stats/stats-page.component.ts - 364 + 365 top: src/app/pages/stats/stats-page.component.ts - 365 + 366 top client src/app/pages/stats/stats-page.component.ts - 365 + 366 Choose stats view. Current view: src/app/pages/stats/stats-page.component.ts - 402 + 403 FSB · Stats src/app/pages/stats/stats-page.component.ts - 483 + 484 Live aggregate adoption, usage, and repository signals for FSB. src/app/pages/stats/stats-page.component.ts - 491 + 492 Cumulative stars src/app/pages/stats/stats-page.component.ts - 978 + 979 Cumulative commits src/app/pages/stats/stats-page.component.ts - 999 + 1000 Tokens (last 30 days) src/app/pages/stats/stats-page.component.ts - 1027 + 1028 Popular MCP clients src/app/pages/stats/stats-page.component.ts - 1050 + 1051 Could not load chart library. src/app/pages/stats/stats-page.component.ts - 1104 + 1105 Could not render the selected chart. src/app/pages/stats/stats-page.component.ts - 1105 + 1106 diff --git a/showcase/angular/src/locale/messages.zh-CN.xlf b/showcase/angular/src/locale/messages.zh-CN.xlf index 179111dc..72206ad8 100644 --- a/showcase/angular/src/locale/messages.zh-CN.xlf +++ b/showcase/angular/src/locale/messages.zh-CN.xlf @@ -5515,7 +5515,7 @@ 上次快照: src/app/pages/dashboard/dashboard-page.component.ts - 3994 + 4014 @@ -5523,7 +5523,7 @@ 状态: src/app/pages/dashboard/dashboard-page.component.ts - 3998 + 4018 @@ -5531,7 +5531,7 @@ 原因: src/app/pages/dashboard/dashboard-page.component.ts - 4002 + 4022 @@ -5539,7 +5539,7 @@ 正在恢复(已持续 秒) src/app/pages/dashboard/dashboard-page.component.ts - 4006 + 4026 @@ -5547,7 +5547,7 @@ 上一帧: 秒前 src/app/pages/dashboard/dashboard-page.component.ts - 4012 + 4032 @@ -5555,7 +5555,7 @@ 变更数: src/app/pages/dashboard/dashboard-page.component.ts - 4013 + 4033 @@ -5563,7 +5563,7 @@ 应用失败次数: src/app/pages/dashboard/dashboard-page.component.ts - 4014 + 4034 @@ -5571,7 +5571,7 @@ 过期变更数: src/app/pages/dashboard/dashboard-page.component.ts - 4015 + 4035 @@ -5579,7 +5579,7 @@ 暂无流数据 src/app/pages/dashboard/dashboard-page.component.ts - 4016 + 4036 @@ -5587,7 +5587,7 @@ 请求失败,状态码为 src/app/pages/dashboard/dashboard-page.component.ts - 4319 + 4339 @@ -5595,7 +5595,7 @@ 约剩 分钟 src/app/pages/dashboard/dashboard-page.component.ts - 4474 + 4494 @@ -5603,7 +5603,7 @@ 约剩 秒 src/app/pages/dashboard/dashboard-page.component.ts - 4475 + 4495 @@ -5611,7 +5611,7 @@ 已运行 src/app/pages/dashboard/dashboard-page.component.ts - 4549 + 4569 @@ -5619,7 +5619,7 @@ 已由用户停止——此前操作: src/app/pages/dashboard/dashboard-page.component.ts - 4554 + 4574 @@ -9542,12 +9542,32 @@ 307 + + September 2026v0.9.91 — 365-day region retention, Region (state-level) metric (full archived text) + 2026 年 9 月v0.9.91 — 地区信息保留 365 天、地区(州级)指标(完整存档文本) + + src/app/pages/privacy/privacy-history-archive.component.html + 3,6 + + + + Archived copy of the privacy policy as it stood from September 28, 2026, before the September 29, 2026 update.TLDR: FSB runs inside your browser, and no browsing data is collected by FSB servers. AI calls go directly from your browser to the provider you choose. API keys and credentials are encrypted locally, and memory data stays on your device. If you install the optional local agent service, FSB can also be driven by an MCP client running on your own machine over a loopback connection, and by coding-agent CLIs you have already installed. The only data that ever leaves your machine for FSB is opt-out anonymous usage telemetry that powers the public /stats page. If you pair Remote Dashboard, transient live-preview frames can pass through the relay during the session, but they are not stored. Everything is open source and auditable.Data CollectionFSB operates entirely within your browser. When you initiate an automation task, the extension may inspect the active tab's DOM (Document Object Model) and, when you invoke a capability, make same-origin site API requests from your browser session.No browsing history is collected or stored beyond the current sessionDOM data and site API results are analyzed locally and discarded after each automation step unless you explicitly save them in memoryNo personal information is harvested from pages you visitSite API capabilitiesFSB's capability layer can call a site's own first-party API from the page context, using your already-authenticated browser session. These requests run locally in Chrome; first-party cookies or site auth may be attached by the browser for that target site, but FSB does not return, store, log, or send cookies, tokens, CSRF values, request bodies, or response bodies to FSB servers.Capability invocations follow FSB's consent controls and are recorded only in the redacted local audit log: origin, capability slug, method, side-effect class, consent decision, and outcome. The audit log never stores invocation arguments, request bodies, response bodies, cookies, tokens, CSRF values, or site response payloads.Chrome permissions FSB requestsTo run web automation, FSB declares the following permissions in its Chrome manifest. Each is used only for the documented purpose; nothing is sent off-device on the strength of any of them.DOM and tabs, activeTab, scripting, tabs, windows, sidePanel, and host permission <all_urls>: read and write the active tab, inject the automation content script, list and switch tabs, and render the side panelAdvanced automation, debugger: attach the Chrome DevTools Protocol for coordinate-based clicks, drag, and key-hold actions that the regular DOM API cannot perform. webNavigation: observe navigation start/finish events so automation waits for the right momentLocal storage, storage, unlimitedStorage: store your settings, credentials, payment methods, and memory in chrome.storage.local on your device. Unlimited storage lifts the default 10 MB quota so memory and session logs can grow without hitting a wallUX helpers, clipboardWrite: write copy-to-clipboard results from automation. alarms: schedule background housekeeping. offscreen: host the speech-to-text recorder in a hidden document because service workers cannot capture audio directlyLocal service, nativeMessaging: start the optional local agent service described under Local Agent Service and Native Messaging below. Nothing about the pages you visit crosses that channel. system.memory: read the total installed RAM figure only, so FSB can suggest a sensible limit on how many agents run at once. The available-memory and per-processor breakdowns are never read, and the figure never leaves the extensionMicrophone access for speech-to-text is not declared in the manifest. Chrome shows its own permission prompt the first time you use the mic button.Data StorageAll settings and data are stored locally in Chrome's extension storage, with sensitive values encrypted at rest as described in the sections below.Configuration is stored in chrome.storage.localAPI keys, credentials, and payment methods are encrypted before storage, each detailed in its own section belowSession logs are stored locally and can be cleared at any timeAnalytics data (task counts, success rates) stays on your deviceExternal ServicesFSB communicates with external AI providers only when you configure and use a hosted provider. If you use LM Studio, AI requests stay on your machine through its local OpenAI-compatible server. The choice of provider and what data is sent is under your control.Hosted API calls are made only to the provider you select (xAI, OpenAI, Anthropic, Google, or OpenRouter)LM Studio uses a local OpenAI-compatible server on your device and does not require an API keySent data includes: task description, DOM structure summary, and action contextIf you pair Remote Dashboard or use legacy Background Agents sync, an optional relay server handles WebSocket messages for that session. Live-preview frames may pass through the relay transiently, but none of the following is ever persisted on an FSB server: page content, DOM data, browsing history, screenshots, AI prompts, AI responses, cookies, tokens, or site API payloads. This is opt-in only, and the rest of this policy refers back to this list rather than restating itEach provider has their own privacy policy governing how they handle API requestsLocal Agent Service and Native MessagingFSB can be driven by an MCP client running on your own machine. That requires a small local agent service which you install yourself, and the nativeMessaging permission exists solely so the extension can start it. Chrome cannot launch a local program directly, so this is the only supported mechanism.The messaging host io.github.fullselfbrowsing.fsb_native_host is registered only by an explicit action you take, by running fsb-mcp-server install --native-host. The extension cannot install it, and its registration names one permitted extension originThe exchange is a closed schema. FSB sends a version number, an action of either wake or bootstrap, and a locally generated correlation identifier; it receives that identifier back with an outcome and a reason drawn from a fixed list. Any additional field is rejected outright and messages are capped at 4 KB. No URL, tab, page content, or DOM data exists anywhere in this schemaA successful start returns a pairing code. This is a loopback authentication secret generated on your device and stored with owner-only permissions, rotated every time the service starts. It carries no information about you or your browsingThe service listens on your machine only. The extension bridge refuses to bind any address that is not loopback, and the MCP endpoint binds 127.0.0.1 by default and whenever FSB starts itUnder its own directory the service keeps only operational records: a diagnostics log of delegated-run outcomes restricted to a fixed field list, and a process-supervision journal. Task text, environment values, binary paths, and error text never reach either of them, and entries carrying credential-shaped values are discarded rather than writtenIf the host is not installed, the feature simply does not start. Nothing is transmitted, and the rest of the extension continues to work normallyDelegated CLI AgentsFSB can hand a task to a coding-agent command-line tool you have already installed, currently Claude Code or Grok Build. FSB starts that program on your own machine; your task is never proxied through FSB servers.Your task text reaches the tool over its standard input and nowhere else. FSB checks this before every run: if the task appears in the command line, the working directory, the environment, or any file path, the run is aborted rather than started. Tasks are capped at 64 KBWhile the run is in progress the tool calls back into FSB over the loopback connection to drive your browser. Whatever it reads that way — page text, DOM snapshots, spreadsheet values, screenshots — is returned to that tool, and therefore reaches its vendor: Anthropic for Claude Code, xAI for Grok BuildThe run uses the account already signed in to that tool. FSB does not hold, read, or transmit your Anthropic or xAI credential, and any usage or charges follow that vendor's own planGrok Build runs inside a profile directory FSB keeps separate from your own. The sign-in credential stored there is written by the Grok tool itself during its own sign-in flowThe agent instructions FSB supplies state that passwords, CVV values, payment-card data, and saved credentials must never enter prompts, narration, logs, or tool argumentsReading and Filling Google SheetsFSB can read and fill a range in Google Sheets by driving the spreadsheet interface in your own already-authenticated browser session.Reading a range returns those cell values to the model you selected, exactly like any other page content FSB reads on your behalf. Only the range you asked for is read; there is no whole-spreadsheet exportSpreadsheet content is stripped out before anything is written to FSB's local session history. Only shape counts survive — how many rows, columns, and values were involved. The spreadsheet address, sheet name, cell references, and every cell value are discardedIf that filtering cannot be applied to a record for any reason, the record is dropped rather than storedRemote Dashboard and PhantomStream Live PreviewWhen you pair Remote Dashboard, FSB can show a live preview of the active browser tab using PhantomStream. The preview streams structured DOM data instead of pixels: an initial snapshot, MutationObserver diffs, scroll position, automation overlays, dialog state, media playback state, and remote-control messages over WebSocket.The relay forwards those live-preview frames only to the paired dashboard session, and nothing from the preview stream is persisted on FSB servers — see the list under External Services aboveFSB enables PhantomStream input masking (maskInputs: true), so passwords and form-control values are masked before they leave the captured pageThe dashboard viewer renders mirrored content in a scriptless sandbox and sanitizes mirrored DOM and CSS before displayImages, video, and audio are mirrored by reference in current dashboard mode. Media bytes do not cross the relay; the viewer may fetch allowed public HTTPS asset or media URLs directly, while private, internal, non-HTTPS, or otherwise blocked origins are replaced with placeholders by PhantomStream's fail-closed fetch policyNo Third-Party TrackingFSB does not include any third-party analytics, ad trackers, or cross-site fingerprinting. There are no cookies and no third-party scripts beyond the AI provider APIs you explicitly configure. The one piece of first-party data FSB sends home is the opt-out Anonymous Usage Telemetry described below, used solely to power the public /stats dashboard.API KeysYour API keys are encrypted locally using AES-GCM before being stored. They are never transmitted anywhere except to the AI provider you configured, and only as authentication headers in API requests.Keys are encrypted at rest in Chrome storageDecryption only happens in-memory when making API callsKeys are never logged, exported, or sharedAuto-PasswordsFSB includes an optional credential manager that stores login credentials encrypted on your device. Passwords are never exposed to AI models. They are filled directly into pages by the content script, bypassing the AI entirely.Credentials are encrypted at rest using AES-GCM with 256-bit keys and PBKDF2 key derivationWhen the AI analyzes a page, password field values are replaced with [hidden]. The actual password is never included in any AI promptAuto-fill is performed by the content script injecting values directly into the DOM, with no AI involvement in the credential flowThe credential list view only shows usernames and domains. Passwords are decrypted individually and only when needed for auto-fillCredentials are stored per-domain with parent domain fallback (e.g., accounts.google.com inherits from google.com)Payment MethodsFSB includes an optional payment-method vault that stores card details on your device for checkout auto-fill. Cards are treated with the same encryption and AI isolation as login credentials, and the full card number is never sent to any AI model.Card details (number, expiry, cardholder, and zip) are encrypted at rest using AES-GCM with the same vault-derived key used for credentialsWhen the AI analyzes a checkout page, any detected card-number field values are replaced with [hidden] before the prompt is built. Card numbers, CVV, and expiry are never included in any AI promptAuto-fill happens via the content script writing directly into the page's DOM fields, bypassing the AI entirelyThe list view shows only a card nickname and last-4 digits. Full numbers are decrypted in memory only at the moment of fillAn MCP client can request a payment fill via use_payment_method, but the user is shown an in-extension confirmation prompt before any card data is written into the pageCVV is never persisted unless you opt in per-card, and even then it is encrypted alongside the rest of the recordSpeech-to-TextFSB includes an optional microphone input for the prompt box. The default provider runs entirely in your browser; an optional OpenAI Whisper fallback can be enabled in settings if you want higher accuracy.Default provider: the browser's native SpeechRecognition API. Audio is processed by Chrome and never leaves your device through FSBOptional Whisper provider: when sttProvider is set to whisper and an OpenAI key is configured, recorded audio chunks are uploaded directly from your browser to OpenAI's transcription endpoint. FSB never sees or stores the audioThe microphone is only active while you are holding or have toggled the mic button. Chrome prompts for permission the first time you use it; FSB does not request microphone access in the extension manifestTranscripts are inserted into the prompt textarea only and are never logged, persisted, or transmitted outside the active AI request you choose to sendDisable speech entirely by leaving the mic button untouched, or by clearing the optional Whisper provider in Chrome extension storagePrompt Injection PreventionWeb pages can contain hidden text designed to hijack AI agents. FSB implements multi-layered defenses to ensure the AI only follows your instructions, never instructions embedded in page content.All page content is wrapped in [PAGE_CONTENT] boundary markers, and the AI is instructed to never follow instructions found within these markersA sanitization engine strips known injection patterns (e.g., "ignore previous instructions", fake system prompts, override attempts) from all page content before it reaches the AIAI-generated actions are validated before execution. Dangerous URLs (javascript:, data:) and script injection attempts are blockedOnly a strict, fixed allowlist of known tools can be executed. The AI cannot invent or call arbitrary actionsContent size is capped (500 chars per value, 15K total prompt cap) to limit payload deliveryInvisible Unicode control characters that websites embed are stripped before processingBackground Agents and Server SyncDeprecated in v0.9.45rc1. FSB's built-in Background Agents have been superseded by OpenClaw and Claude Routines, with remote control now handled by the Sync tab. The disclosures below are retained for users still running v0.9.44 or earlier; on current builds the relay server is only contacted when you pair a Sync session.If you opt into legacy Background Agents server sync or pair Remote Dashboard, a relay server facilitates communication between your extension and the dashboard.The server stores: agent definitions (name, schedule, target URL), run metrics (token count, cost, duration, success/fail status), and session pairing tokensBeyond that run metadata the server persists nothing derived from the pages you visit — see the list under External Services aboveAuthentication uses hash keys (generated locally) and session tokens that expire after 24 hoursOne-time pairing tokens expire after 60 seconds and cannot be reusedServer sync is disabled by default. You must explicitly enable it in OptionsMemory SystemFSB's memory system stores navigation patterns and site intelligence to improve automation over time.All memory data (semantic, episodic, procedural) is stored locally in chrome.storage.localNo memory data is sent to any external serverMemory can be viewed and cleared at any time from the Options dashboardSite maps and navigation patterns are domain-specific and isolated from each otherSession Replay and ScreenshotsWhen an MCP client drives FSB, the extension records what the agent did so you can review or replay the run afterwards. These records stay on your device.Runs still in progress are held in Chrome's session storage so they survive the service worker being evicted; finished runs are written to chrome.storage.local through the same history store your own automation runs useRecords are redacted before they are stored. Values under credential-shaped keys, text typed into fields that look like password, one-time-code, or card inputs, and signed or token-bearing URL parameters are all replaced. Ordinary addresses and selectors are preserved, because replay does not work without themRecorded runs are kept for 30 days by default, adjustable between 1 and 365 days, and older ones are pruned by a daily job. Recording can be switched off entirely in Advanced SettingsScreenshot images are never written into that history. Only capture metadata such as dimensions and byte size is retainedScreenshots an MCP client requests are saved by the local service as files on your own disk with owner-only permissions and deleted automatically after seven days. The image is also handed back to the requesting client, and therefore to that client's modelAnonymous Usage TelemetryFSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.What we collectA random per-install UUID stored in chrome.storage.local under the key fsbInstallUuid. The UUID is generated locally and never tied to your identity.The name of the MCP client used (e.g. Claude Code, Cursor, Codex), drawn from a fixed allowlist.The model name used for a session (e.g. grok-4-fast, claude-opus-4), drawn from a fixed allowlist.Aggregate input/output token counts per session.The number of active FSB agents on your install (an integer count).What we do NOT collectPage URLs, hostnames, or browsing history.Prompts, instructions, task descriptions, or any natural-language text you send to your model provider.Page DOM, screenshots, page content, site API payloads, or AI responses.Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.Names, usernames, account handles, or any free-form identity fields.Email addresses, phone numbers, or contact information.Region (state-level) metricThe server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs.The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).RetentionRaw events are retained for 7 days. Daily rollups (one row per install per day, including the coarse region label) are retained for 365 days. Global aggregates (one row per day, no per-install dimension) are retained indefinitely so historical charts on /stats remain stable.How to opt outOpen the FSB Control Panel, scroll to Advanced Settings, and toggle Send anonymous usage data off. The change takes effect immediately; no further events will be sent from your install.How to erase your dataTo request erasure of all telemetry rows associated with your install (GDPR Article 17), look up your fsbInstallUuid in Chrome DevTools → Application → Storage → Extension storage, then send a single HTTP request:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Consent and Audit ControlsFSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. + 这是自 2026 年 9 月 28 日起生效的隐私政策的存档副本,内容为 2026 年 9 月 29 日更新前的版本。摘要: FSB 在你的浏览器内运行,FSB 服务器不会收集任何浏览数据。AI 调用直接从你的浏览器发送到你选择的提供商。API 密钥和凭据在本地加密,记忆数据保留在你的设备上。如果你安装了可选的本地代理服务,FSB 还可以由运行在你自己机器上、通过回环连接通信的 MCP 客户端驱动,以及由你已安装的编码代理 CLIs 驱动。为 FSB 而离开你机器的唯一数据,是支撑公开页面 /stats 的可选择退出的匿名使用情况遥测。Remote Dashboard 配对后,短暂的实时预览帧可能在会话期间经过中继,但不会被存储。一切均为开源且可审计。数据收集FSB完全在浏览器内运行 。 当您启动自动化任务时, 扩展可检查活动标签DOM(文件对象模型),并在援引能力时,建立同源网站API浏览器会话中的请求。不收集或保存当前会话之外的任何浏览历史DOM数据和网址API本地分析结果,并在每个自动化步骤后丢弃,除非您明确保存在内存中不从你访问的页面采集任何个人信息站点API能力FSB 的能力层可在页面上下文中使用您已通过身份验证的浏览器会话,调用网站自身的第一方 API。这些请求会在 Chrome 中本地运行;浏览器可能会附加该目标网站的第一方 Cookie 或网站身份验证信息,但 FSB 不会将 Cookie、令牌、CSRF 值、请求正文或响应正文返回、存储、记录或发送到 FSB 服务器。能力调用会遵循 FSB 的同意控制,且仅记录在经过脱敏的本地审计日志中:来源、能力标识、方法、副作用类别、同意决定与结果。审计日志绝不会存储调用参数、请求正文、响应正文、Cookie、令牌、CSRF 值或网站响应载荷。Chrome 权限中 FSB 请求的项目为了执行网页自动化,FSB 在其 Chrome 清单中声明以下权限。每项仅用于其记录的用途;不会以任何一项为依据将数据发送至设备外。DOM 和标签页、activeTab、scripting、tabs、windows、sidePanel,以及主机权限 <all_urls>:读取和写入当前标签页,注入自动化内容脚本,列出并切换标签页,以及呈现侧边栏高级自动化,debugger:挂接 Chrome DevTools Protocol,以执行常规 DOM API 无法完成的基于坐标的点击、拖动和长按按键操作。webNavigation:监测导航开始和结束事件,使自动化在正确时机继续本地存储 ,storage,unlimitedStorage:将您的设置,证书,支付方法,以及内存存储在设备上的 chrome.storage.local. 无限制存储可以解除默认的 10 MB 配额, 这样内存和会话日志可以在不撞墙的情况下增长UX帮助者,clipboardWrite:通过自动化将复制到剪贴板的结果写入. alarms:安排背景内务。 offscreen:在隐藏文档中托管语音对文本记录器,因为服务人员无法直接捕捉音频本地服务, nativeMessaging:启动下方 本地代理服务与原生消息传递 中描述的可选本地代理服务。你访问的页面信息不会经过该通道。system.memory:仅读取已安装的 RAM 总量,以便 FSB 建议合理的并发代理数量上限。可用内存和各处理器的明细从不读取,该数值也从不离开扩展程序语音转文字所需的麦克风访问权限未在清单中声明。当你首次使用麦克风按钮时,Chrome 会自行弹出权限请求。数据存储所有设置和数据都本地存储在 Chrome 的扩展程序存储中,敏感值按下文各节所述在静态时加密。配置保存在 chrome.storage.local 中API 密钥、凭据和支付方式在存储前均已加密,各自的细节见下文对应章节会话日志保存在本地,可随时清除分析数据(任务计数、成功率)保留在你的设备上外部服务FSB 仅在你配置并使用托管提供商时,才与外部 AI 提供商通信。如果你使用 LM Studio,AI 请求会通过其本地 OpenAI 兼容服务器留在本机。提供商选择与发送的数据完全由你掌控。托管 API 调用只会发往你选择的提供商(xAI、OpenAI、Anthropic、Google 或 OpenRouter)LM Studio 在你的设备上使用本地 OpenAI 兼容服务器,且无需 API Key发送的数据包括:任务说明,DOM结构摘要和行动背景如果你配对 Remote Dashboard 或使用旧版 Background Agents 同步,可选的中继服务器会处理该会话的 WebSocket 消息。实时预览帧可能短暂经过中继,但以下内容绝不会保存在 FSB 服务器上:页面内容、DOM 数据、浏览历史、屏幕截图、AI 提示、AI 响应、Cookie、令牌或站点 API 负载。此功能仅为选择加入,本政策其余部分引用此列表而不再重复每个提供商都有自己的隐私政策,规定其如何处理 API 请求本地代理服务与原生消息传递FSB 可由运行在你自己机器上的 MCP 客户端驱动。这需要一个由你自行安装的小型本地代理服务,而 nativeMessaging 权限的存在仅仅是为了让扩展程序能够启动它。Chrome 无法直接启动本地程序,因此这是唯一受支持的机制。消息主机 io.github.fullselfbrowsing.fsb_native_host 仅通过你的明确操作注册,即运行 fsb-mcp-server install --native-host。扩展程序无法安装它,且其注册仅列出一个被允许的扩展程序来源该交换采用封闭模式。FSB 发送一个版本号、一个为 wake 或 bootstrap 的操作,以及一个本地生成的关联标识符;返回的是该标识符,附带取自固定列表的结果和原因。任何额外字段都会被直接拒绝,消息上限为 4 KB。该模式中不存在任何 URL、标签页、页面内容或 DOM 数据成功启动后会返回一个配对码。它是在你的设备上生成、以仅限所有者的权限保存、并在每次服务启动时轮换的回环认证密钥。其中不包含任何关于你或你浏览行为的信息该服务仅在你的机器上监听。扩展程序桥接拒绝绑定任何非回环地址,而 MCP 端点在默认情况下以及每当由 127.0.0.1 启动时都绑定 FSB该服务在自己的目录中仅保留运行记录:一份限定于固定字段列表的委派运行结果诊断日志,以及一份进程监督日志。任务文本、环境变量值、二进制路径和错误文本都不会进入其中任何一个,含有疑似凭据值的条目会被丢弃而非写入如果未安装该主机,此功能就不会启动。不会传输任何内容,扩展程序的其余部分照常运行委派的 CLI 代理FSB 可以把任务交给你已安装的编码代理命令行工具,目前为 Claude Code 或 Grok Build。FSB 会在你自己的机器上启动该程序;你的任务绝不会经由 FSB 服务器转发。你的任务文本仅通过工具的标准输入传递,别无他途。FSB 在每次运行前都会检查这一点:若任务出现在命令行、工作目录、环境变量或任何文件路径中,该次运行会被中止而非启动。任务上限为 64 KB运行期间,该工具会通过回环连接回调 FSB 以驱动你的浏览器。它由此读取的一切内容——页面文本、DOM 快照、电子表格数值、屏幕截图——都会返回给该工具,因而也就到达其供应商:Anthropic 对应 Claude Code,xAI 对应 Grok Build该次运行使用你已在该工具中登录的账户。FSB 不会保存、读取或传输你的 Anthropic 或 xAI 凭据,任何用量或费用均遵循该供应商自身的方案Grok Build 在 FSB 与你自己的目录分开保管的配置文件目录中运行。存放在那里的登录凭据由 Grok 工具在其自身的登录流程中写入由 FSB 提供的代理指令规定,密码、CVV 值、支付卡数据和已保存的凭据绝不得进入提示、叙述、日志或工具参数读取与填写 Google SheetsFSB 可以在你已通过身份验证的浏览器会话中操作电子表格界面,从而读取和填写 Google Sheets 中的某个区域。读取某个区域时,这些单元格数值会发送给你所选择的模型,与 FSB 代表你读取的任何其他页面内容完全一样。只会读取你请求的区域;不存在整表导出在向 FSB 的本地会话历史写入任何内容之前,电子表格内容都会被剥离。仅保留规模计数——涉及多少行、多少列、多少个数值。电子表格地址、工作表名称、单元格引用以及每一个单元格数值都会被丢弃如果由于任何原因无法对某条记录应用该过滤,则该记录会被丢弃而非存储Remote Dashboard和PhantomStream现场预览配对 Remote Dashboard 时,FSB 可使用 PhantomStream 显示当前浏览器标签页的实时预览。预览流传输结构化 DOM 数据而非像素,包括初始快照、MutationObserver 差异、滚动位置、自动化叠加层、对话框状态、媒体播放状态,以及通过 WebSocket 传输的远程控制消息。中继仅将这些实时预览帧转发给已配对的仪表板会话,预览流中的任何内容都不会保存在 FSB 服务器上——参见上文 外部服务中的列表FSB 会启用 PhantomStream 输入内容遮蔽(maskInputs: true),因此密码和表单控件的值会在离开被捕获页面前被遮蔽仪表盘查看器将镜像内容装在一个无脚本的沙盒中并对镜像进行消毒DOM和CSS显示前在目前的仪表板模式下,图像,视频,和音频通过参考镜像. 媒体字节不通过中继器; 查看器可能获取允许的公开HTTPS资产或媒体 URL 直接,而私人、内部、非HTTPS,或以其他方式被封存的源头替换为占位符PhantomStream失败的获取策略无第三方跟踪FSB 不包含任何第三方分析、广告跟踪器或跨站点指纹识别。除你显式配置的 AI 提供商 APIs 之外,没有 Cookie,也没有第三方脚本。FSB 向自身发送的唯一第一方数据是下面描述的可选退出的匿名使用遥测,仅用于驱动公开的 /stats 仪表板。API Key你的 API Key 会在保存前通过 AES-GCM 在本地加密。除了你配置的 AI 提供商之外,绝不会发送到任何地方,且只在 API 请求的身份验证头中使用。Key 在 Chrome 存储中以加密形式静态保存仅在 API 调用时在内存中解密Key 绝不会被记录、导出或共享自动密码FSB 提供可选的凭据管理器,将登录凭据加密保存到你的设备。密码不会暴露给 AI 模型,而是由内容脚本直接填入页面,完全绕过 AI。凭据静态存储时使用 AES-GCM 和 256 位密钥,并通过 PBKDF2 派生密钥进行加密AI 分析页面时,密码字段的值会被替换为 [hidden]。真实密码绝不会出现在任何 AI 提示中自动填充由内容脚本直接插入到DOM 中,没有AI参与证书流凭据列表视图仅显示用户名和域名。密码只在自动填充需要时单独解密凭据按域名存储,并具备父域回退(例如 accounts.google.com 会继承 google.com)付款方式FSB 包含一个可选的付款方式保险库,将卡片信息保存在你的设备上以便结账时自动填写。卡片采用与登录凭据相同的加密与 AI 隔离机制,完整的卡号绝不会发送给任何 AI 模型。卡片信息(卡号、有效期、持卡人和邮编)使用与凭据相同的保险库派生密钥,通过 AES-GCM 在静态时加密当 AI 分析结账页面时,检测到的卡号字段值会在构建提示词之前被替换为 [hidden]。卡号、CVV 和有效期绝不会包含在任何 AI 提示词中自动填写由内容脚本直接写入页面的 DOM 字段完成,完全绕过 AI列表视图仅显示卡片别名与后 4 位数字。完整卡号仅在填写的那一刻在内存中解密MCP 客户端可以通过 use_payment_method 请求执行付款自动填写,但在任何卡片数据写入页面之前,会在扩展内向用户显示确认提示除非你为某张卡片明确启用,否则 CVV 永远不会被持久保存;即使启用,它也会与记录的其余部分一起加密语音转文字FSB 在提示词输入框中包含一个可选的麦克风输入。默认提供商完全在你的浏览器中运行;如果你需要更高的准确性,可在设置中启用可选的 OpenAI Whisper 后备方案。默认提供者: 浏览器的本地SpeechRecognition API。音频由Chrome永远不要离开你的设备FSB可选的 Whisper 提供商:当 sttProvider 设置为 whisper 且配置了 OpenAI 密钥时,录制的音频片段会从你的浏览器直接上传到 OpenAI 的转录接口。FSB 从不查看或存储该音频麦克风仅在你按住或已切换开启麦克风按钮的期间处于活动状态。首次使用时 Chrome 会请求权限;FSB 不会在扩展清单中请求麦克风访问权限转录结果只插入到提示词文本框中,绝不会在你选择发送的活跃 AI 请求之外被记录、保存或传输通过不触碰麦克风按钮,或在 Chrome 扩展存储中清除可选的 Whisper 提供商,即可完全禁用语音功能防止提示词注入网页可能包含旨在劫持 AI 智能体的隐藏文本。FSB 通过多层防御确保 AI 只遵循你的指令,绝不执行嵌入在页面内容中的指令。所有页面内容都被包裹在 [PAGE_CONTENT] 边界标记内,并指示 AI 永远不要执行其中的指令在内容到达 AI 之前,清洗引擎会剥离已知的注入模式(例如“忽略以上指令”、伪造的系统提示、覆盖请求)AI 生成的动作会在执行前进行验证。危险 URL(javascript:、data:)和脚本注入尝试将被阻止只能执行严格且固定的已知工具白名单中的操作。AI 无法自行构造或调用任意操作内容大小有上限(单值 500 字符,提示总上限 15K),限制有效载荷投递网站嵌入的不可见 Unicode 控制字符会在处理前被剥离后台智能体与服务器同步已在 v0.9.45rc1 中弃用。 FSB 内置后台代理已由 OpenClaw 和 Claude Routines 取代,远程控制现在由“同步”标签页处理。下列说明保留给仍使用 v0.9.44 或更早版本的用户;在当前版本中,只有在配对“同步”会话时才会连接至中继服务器。如果您选择使用遗留的背景代理服务器同步或对齐Remote Dashboard,中继服务器将促进您的扩展和仪表板之间的通信。服务器会保存:智能体定义(名称、计划、目标 URL)、运行指标(Token 数、成本、时长、成功/失败状态)、会话配对令牌除那些运行元数据之外,服务器不会保存任何源自你所访问页面的内容——参见上文 外部服务中的列表身份验证使用本地生成的哈希 Key,以及 24 小时后失效的会话令牌一次性配对令牌在 60 秒后失效,无法重复使用服务器同步默认关闭,需要你在“选项”中显式启用记忆系统FSB 的记忆系统会沉淀导航模式和站点情报,让自动化随时间持续优化。所有记忆数据(语义、情景、过程)都保存在 chrome.storage.local 中记忆数据不会发送到任何外部服务器你可在选项控制台中随时查看并清除记忆站点地图和导航模式按域名隔离,互不影响会话回放与屏幕截图当 MCP 客户端驱动 FSB 时,扩展程序会记录代理的操作,以便你之后查看或回放该次运行。这些记录保留在你的设备上。仍在进行中的运行保存在 Chrome 的会话存储中,以便在 service worker 被回收后依然留存;已完成的运行则通过 chrome.storage.local 写入与你自己的自动化运行相同的历史存储记录在存储前会先经过脱敏。疑似凭据键下的数值、输入到形似密码、一次性验证码或银行卡字段中的文本,以及带签名或携带令牌的 URL 参数,都会被替换。普通地址和选择器会保留,因为没有它们回放就无法进行已记录的运行默认保留 30 天,可在 1 至 365 天之间调整,较旧的记录由每日任务清理。可在高级设置中完全关闭记录功能屏幕截图图像绝不会写入该历史记录。仅保留尺寸、字节大小等捕获元数据由 MCP 客户端请求的屏幕截图,会由本地服务以仅限所有者的权限保存为你自己磁盘上的文件,并在七天后自动删除。该图像同时也会回传给发起请求的客户端,因而也会到达该客户端的模型匿名使用遥测FSB v0.9.69 引入了一个可选择退出的匿名使用情况遥测管道,以便项目可以发布汇总的采用数据(参见 /stats),且绝不触及你浏览的页面。唯一的位置信号是服务器在接收时从你的请求 IP 派生出的粗略国家/州标签(绝不来自页面内容),且仅以汇总形式公开——参见下方的 地区(州级)指标。遥测默认开启,但可通过单个开关禁用,并且可应请求删除每次安装的数据。我们收集的内容每次安装随机生成的 UUID,存储在 chrome.storage.local 的键 fsbInstallUuid 下。UUID 在本地生成,永不与你的身份关联。所使用的 MCP 客户端名称(例如 Claude Code、Cursor、Codex),取自固定的允许列表。会话中使用的模型名称(例如 grok-4-fast、claude-opus-4),取自固定的允许列表。每个会话的输入/输出 token 数量聚合值。你的安装上活跃的 FSB 代理数量(一个整数值)。我们不收集的内容页面 URLs、主机名或浏览历史。提示、指令、任务描述或你发送给模型提供商的任何自然语言文本。页面DOM,截图,页面内容,站点API有效载荷,或AI响应.明文 IP 地址。服务器仅出于三个目的临时且内联地使用请求 IP——用于限流的每日轮换加盐哈希、派生粗略的国家/州标签(见下文),以及记录该地址是 IPv4 还是 IPv6——随后立即将其丢弃。明文 IP 绝不会被存储或记录。姓名、用户名、账号 handle 或任何自由文本身份字段。电子邮件地址、电话号码或联系信息。地区(州级)指标服务器在接收请求时,根据请求的 IP,使用自托管的 DB-IP IP-to-City Lite 数据集和自有查找逻辑,推导出粗略的国家及美国州(行政区)标签——这并非实时第三方地理定位服务,也绝不会使用 MaxMind。明文 IP 会被即时处理并丢弃;仅保留推导出的标签和地址族(IPv4 或 IPv6)。地区仅以汇总形式公开,且位于 k≥5 的匿名阈值之后:不同安装数少于 5 的任何州都会合并到单个“Other”桶中(当该合并计数本身低于 5 时则完全抑制)。任何公开的州标签都绝不会代表少于 5 次安装。粗略标签存储在原始事件(由 7 天保留策略删除)以及你的安装的每日汇总中;每日汇总保留 365 天,以便 /stats 按每次安装最近的地区只计数一次。该标签绝不是 IP 地址、城市或坐标;当你请求删除时,它会与你安装的其余数据一并删除;并且它只会在上述 k≥5 阈值之后公开。地理定位数据来自 DB-IP(https://db-ip.com)。保留期原始事件保留 7 天。每日汇总(每安装每天一行,包括粗略的地区标签)保留 365 天。全局聚合(每天一行,不按安装维度)无限期保留,以便 /stats 上的历史图表保持稳定。如何选择退出打开 FSB 控制面板,滚动到"高级设置",将 发送匿名使用数据 开关关闭。更改立即生效;之后不会再从你的安装发送事件。如何擦除你的数据如要请求删除与您的安装相关的所有遥测数据行(GDPR 第 17 条),请查找您的 fsbInstallUuid:前往 Chrome DevTools → 应用程序 → 存储 → 扩展程序存储,然后发送一次 HTTP 请求:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合规声明FSB 的匿名使用遥测仅用于计算在 full-selfbrowsing.com/stats 公开展示的聚合使用统计。数据从不出售、从不与第三方共享、从不用于广告,且从不用于训练任何机器学习模型。此承诺满足 Chrome Web Store 的 Limited Use 要求。聚合的公开指标我们在 /stats 发布来自此遥测管道的聚合指标。仅展示计数和总数;每安装的行从不暴露。同意和审计控制FSB 的自动化策略采用选择退出模式:未列入拒绝列表的来源会继承用户可配置的全局默认设置,当前默认出厂设置为 Auto;明确的按来源策略则可将来源设置为 Off、Ask 或 Auto。审计日志仅在本地保留且有期限,控制面板也提供导出与清除功能。服务拒绝列表仍是硬性限制。无论全局默认值或已存储的按来源策略为何,被拒绝的来源都无法启用。敏感源可以运行在 Auto 下读取,但在执行前写着重新强制每个源的变异选择。 非敏感来源可选择退出或移至控制小组的同意和审计科。每次能力调用都会记录在经过脱敏的、仅可追加的本地审计日志中。绝不会存储任何参数、令牌、Cookie 或响应正文。开源FSB在MIT下完全开源 执照 你可以对每条代码进行审核 以核实这些隐私诉求 源代码可见于GitHub.本政策的变更若本政策更新,变更将体现在页面顶部的“最后更新”日期。重要变更也会在项目的 GitHub 发布说明中注明。联系方式如果您对此项隐私政策或FSB的数据处理有疑问,请在GitHub Issues上打开一个问题。 + + src/app/pages/privacy/privacy-history-archive.component.html + 7,238 + + August 2026v0.9.91 — Local Agent Service and Native Messaging, Delegated CLI Agents, Google Sheets, Session Replay and Screenshots (full archived text) 2026 年 8 月v0.9.91 — 本地代理服务与原生消息传递、委派的 CLI 代理、Google Sheets、会话回放与屏幕截图(完整存档文本) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 242,245 @@ -9559,7 +9579,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合规声明FSB 的匿名使用遥测仅用于计算在 full-selfbrowsing.com/stats 公开展示的聚合使用统计。数据从不出售、从不与第三方共享、从不用于广告,且从不用于训练任何机器学习模型。此承诺满足 Chrome Web Store 的 Limited Use 要求。聚合的公开指标我们在 /stats 发布来自此遥测管道的聚合指标。仅展示计数和总数;每安装的行从不暴露。同意和审计控制FSB 的自动化策略采用选择退出模式:未列入拒绝列表的来源会继承用户可配置的全局默认设置,当前默认出厂设置为 Auto;明确的按来源策略则可将来源设置为 Off、Ask 或 Auto。审计日志仅在本地保留且有期限,控制面板也提供导出与清除功能。服务拒绝列表仍是硬性限制。无论全局默认值或已存储的按来源策略为何,被拒绝的来源都无法启用。敏感源可以运行在 Auto 下读取,但在执行前写着重新强制每个源的变异选择。 非敏感来源可选择退出或移至控制小组的同意和审计科。每次能力调用都会记录在经过脱敏的、仅可追加的本地审计日志中。绝不会存储任何参数、令牌、Cookie 或响应正文。开源FSB在MIT下完全开源 执照 你可以对每条代码进行审核 以核实这些隐私诉求 源代码可见于GitHub.本政策的变更若本政策更新,变更将体现在页面顶部的“最后更新”日期。重要变更也会在项目的 GitHub 发布说明中注明。联系方式如果您对此项隐私政策或FSB的数据处理有疑问,请在GitHub Issues上打开一个问题。 src/app/pages/privacy/privacy-history-archive.component.html - 7,238 + 246,477 @@ -9567,7 +9587,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026 年 5 月 – 7 月v0.9.90 — 站点 API 能力、Remote Dashboard 与 PhantomStream、支付方式、语音转文字、地区指标(完整存档文本) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 481,484 @@ -9579,7 +9599,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合规声明FSB的匿名使用情况遥测只用于计算公开显示在 full-selfbrowsing.com/stats的汇总使用情况统计。数据绝不出售、绝不与第三方分享、绝不用于广告,也绝不用于训练任何机器学习模型。此承诺符合 Chrome Web Store Limited Use 要求。公开汇总指标我们在 /stats发布由此遥测流程生成的汇总指标。只会显示计数与总数,绝不公开单独安装的数据行。同意和审计控制FSB的自动化策略采用选择退出模式:未列入拒绝列表的来源会继承用户可配置的全局默认设置,当前默认出厂设置为 Auto;明确的按来源策略则可将来源设置为 Off、Ask 或 Auto。审计日志仅在本地保留且有期限,控制面板也提供导出与清除功能。服务拒绝列表仍是硬性限制。无论全局默认值或已存储的按来源策略为何,被拒绝的来源都无法启用。敏感源可以运行在 Auto 下读取,但在执行前写着重新强制每个源的变异选择。 非敏感来源可选择退出或移至控制小组的同意和审计科。每次能力调用都会记录在经过脱敏、仅可追加的本地审计日志中。任何参数、令牌、Cookie 或响应正文都不会被存储。开源FSB 完全采用 MIT 许可证开源。您可以审计每一行代码,验证这些隐私声明。源代码可在 GitHub。本政策的变更如果本政策更新,变更会反映在页面顶部的“上次更新”日期中。重大变更也会记录在项目的 GitHub 发行说明中。联系方式如果您对本隐私政策或 FSB的数据处理方式有任何疑问,请在 GitHub Issues。 src/app/pages/privacy/privacy-history-archive.component.html - 7,198 + 485,676 @@ -9587,7 +9607,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026 年 3 月v9.0.2 — 后台智能体、记忆系统、服务器同步、匿名使用情况遥测、法律立场(完整存档文本) src/app/pages/privacy/privacy-history-archive.component.html - 202,205 + 680,683 @@ -9599,7 +9619,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合规声明FSB 的匿名使用情况遥测只用于计算公开显示在 full-selfbrowsing.com/stats 的汇总使用情况统计。数据绝不出售、绝不与第三方分享、绝不用于广告,也绝不用于训练任何机器学习模型。此承诺符合 Chrome Web Store 的 Limited Use 要求。公开汇总指标我们在 /stats 发布由此遥测流程生成的汇总指标。只会显示计数与总数,绝不公开单独安装的数据行。法律立场与同意模式FSB 的自动化安全策略、审计日志保留方式和按来源同意模式,均通过明确的产品控件呈现。FSB 在您明确允许前,不会对任何来源执行操作;自动读取权限绝不代表写入权限,保守的服务拒绝列表会彻底阻止对敏感类别(金融和政府服务)的自动化。按来源同意默认关闭。Auto 与写入(更改)权限是两个独立且需要明确启用的选项,可在控制面板的“同意与审计”部分管理。每次能力调用都会记录在经过脱敏、仅可追加的本地审计日志中。任何参数、令牌、Cookie 或响应正文都不会被存储。服务拒绝列表使敏感来源无法启用;此限制由能力门控强制执行,而非仅体现在界面中。开源FSB 完全采用 MIT 许可证开源。您可以审计每一行代码,验证这些隐私声明。源代码可在 GitHub 获取。本政策的变更如果本政策更新,变更会反映在页面顶部的“上次更新”日期中。重大变更也会记录在项目的 GitHub 发行说明中。联系方式如果您对本隐私政策或 FSB 的数据处理方式有任何疑问,请在 GitHub Issues 提出问题。 src/app/pages/privacy/privacy-history-archive.component.html - 206,345 + 684,823 @@ -9607,7 +9627,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026 年 2 月v0.9 — 初始隐私政策(完整存档文本) src/app/pages/privacy/privacy-history-archive.component.html - 349,352 + 827,830 @@ -9615,7 +9635,7 @@ https://full-selfbrowsing.com/api/telemetry/forget这是 2026 年 2 月初版隐私政策的存档副本。此快照重现自原始静态展示页面,已移除页面框架并保留政策内容。数据收集FSB 完全在您的浏览器内运行。扩展程序只会在您启动自动化任务时,访问当前活动标签页的 DOM(文档对象模型)。不会收集或存储当前会话以外的浏览记录DOM 数据会在本地分析,并在每个自动化步骤后丢弃不会从您访问的页面收集个人信息数据存储所有设置与数据皆存储在 Chrome 的扩展程序存储空间中。FSB 使用 AES-GCM 加密 API 密钥等敏感数据。设置存储在 chrome.storage.localAPI 密钥在存储前会使用加密算法 AES-GCM会话日志存储在本地,可随时清除分析数据(任务数量、成功率)会保留在您的设备上外部服务FSB 只会在您设置并使用外部 AI 供应商时与其通信。您可以自行控制所选供应商及发送的数据。API 调用只会发送到您选择的供应商(xAI、OpenAI、Anthropic 或 Google)发送的数据包括:任务描述、DOM 结构摘要和操作上下文不会将数据发送到 FSB 服务器,因为根本不存在这类服务器每个供应商都有自己的隐私政策,规范其处理 API 请求的方式不跟踪FSB 不包含任何分析、遥测或跟踪服务。除了您明确设置的 AI 供应商 API,不会使用 Cookie、浏览器指纹或第三方脚本。API 密钥您的 API 密钥会在存储前于本地使用 AES-GCM 加密。除了您设置的 AI 供应商外,绝不会发送到其他地方,且只会作为 API 请求中的身份验证请求头。密钥在 Chrome 存储空间中以加密形式静态存储只有在发出 API 调用时才会在内存中解密密钥绝不会被记录、导出或分享自动密码测试版FSB 提供可选的凭据管理器,将登录凭据加密存储在您的设备上。密码绝不会暴露给 AI 模型,而是由内容脚本直接填入页面,完全绕过 AI。凭据静态存储时会使用 AES-GCM 和 256 位密钥,并通过 PBKDF2 派生密钥进行加密AI 分析页面时,密码字段的值会替换为 [hidden];实际密码绝不会包含在任何 AI 提示中内容脚本会直接将值注入 DOM ,以完成自动填充,整个凭据流程不涉及 AI凭据清单只会显示用户名与域名;密码会单独解密,且只在自动填充需要时进行凭据按域名存储,并以父域名作为回退(例如 accounts.google.com 继承自 google.com)提示注入防护网页可能包含企图劫持 AI 代理的隐藏文本。FSB 采用多层防护,确保 AI 只遵循您的指令,绝不遵循嵌入页面内容的指令。所有页面内容都会包在 [PAGE_CONTENT] 边界标记内,并明确要求 AI 绝不遵循这些标记内的指令页面内容发送给 AI 前,清理引擎会移除已知的注入模式(例如“忽略先前的指示”、伪造的系统提示及覆盖尝试)AI 生成的操作会在运行前验证;危险 URL(javascript:、data:)和脚本注入尝试都会被阻止只能运行严格白名单中的 30 多项已知工具;AI 无法自行发明或调用任意操作内容大小设有上限(每个值 500 个字符,提示总计上限 15K),以限制载荷传播网站嵌入的不可见 Unicode 控制字符会在处理前移除开源FSB 完全采用 MIT 许可证开源。您可以审计每一行代码,验证这些隐私声明。源代码可在 GitHub 获取。在 GitHub 上查看源代码摘要FSB 会在本地处理数据、加密敏感信息,绝不向 AI 模型暴露密码,防范提示注入攻击,只与您选择的 AI 供应商通信,不含任何跟踪功能,并以开源形式提供完整审计能力。 src/app/pages/privacy/privacy-history-archive.component.html - 353,420 + 831,898 @@ -9635,8 +9655,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Last updated: September 2026 - 最后更新:2026 年 9 月 + Last updated: September 29, 2026 + 最后更新:2026 年 9 月 29 日 src/app/pages/privacy/privacy-page.component.html 6,9 @@ -10563,8 +10583,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. - FSB v0.9.69 引入了一个可选择退出的匿名使用情况遥测管道,以便项目可以发布汇总的采用数据(参见 /stats),且绝不触及你浏览的页面。唯一的位置信号是服务器在接收时从你的请求 IP 派生出的粗略国家/州标签(绝不来自页面内容),且仅以汇总形式公开——参见下方的 地区(州级)指标。遥测默认开启,但可通过单个开关禁用,并且可应请求删除每次安装的数据。 + FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse place label — city, state or province, and country — that the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (city-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. + FSB v0.9.69 引入了一个可选择退出的匿名使用情况遥测管道,以便项目可以发布汇总的采用数据(参见 /stats),且绝不触及你浏览的页面。唯一的位置信号是一个粗略的地点标签(城市、州或省,以及国家),由服务器在接收时从你的请求 IP 派生而来(绝不来自页面内容),且仅以汇总形式公开——参见下方的 地区(城市级)指标。遥测默认开启,但可通过单个开关禁用,并且可应请求删除每次安装的数据。 src/app/pages/privacy/privacy-page.component.html 197,199 @@ -10651,8 +10671,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. - 明文 IP 地址。服务器仅出于三个目的临时且内联地使用请求 IP——用于限流的每日轮换加盐哈希、派生粗略的国家/州标签(见下文),以及记录该地址是 IPv4 还是 IPv6——随后立即将其丢弃。明文 IP 绝不会被存储或记录。 + Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse city, state, and country label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. + 明文 IP 地址。服务器仅出于三个目的临时且内联地使用请求 IP——用于限流的每日轮换加盐哈希、派生粗略的城市/州/国家标签(见下文),以及记录该地址是 IPv4 还是 IPv6——随后立即将其丢弃。明文 IP 绝不会被存储或记录。 src/app/pages/privacy/privacy-page.component.html 213,214 @@ -10675,43 +10695,51 @@ https://full-selfbrowsing.com/api/telemetry/forget - Region (state-level) metric - 地区(州级)指标 + Region (city-level) metric + 地区(城市级)指标 src/app/pages/privacy/privacy-page.component.html 218,220 - The server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. - 服务器在接收请求时,根据请求的 IP,使用自托管的 DB-IP IP-to-City Lite 数据集和自有查找逻辑,推导出粗略的国家及美国州(行政区)标签——这并非实时第三方地理定位服务,也绝不会使用 MaxMind。明文 IP 会被即时处理并丢弃;仅保留推导出的标签和地址族(IPv4 或 IPv6)。 + The server derives a coarse place label — country, state or province, and city — from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. + 服务器在接收请求时,根据请求的 IP,使用自托管的 DB-IP IP-to-City Lite 数据集和自有查找逻辑,推导出粗略的地点标签(国家、州或省,以及城市)——这并非实时第三方地理定位服务,也绝不会使用 MaxMind。明文 IP 会被即时处理并丢弃;仅保留推导出的标签和地址族(IPv4 或 IPv6)。 src/app/pages/privacy/privacy-page.component.html 220,221 - Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs. - 地区仅以汇总形式公开,且位于 k≥5 的匿名阈值之后:不同安装数少于 5 的任何州都会合并到单个“Other”桶中(当该合并计数本身低于 5 时则完全抑制)。任何公开的州标签都绝不会代表少于 5 次安装。 + Region is published only in aggregate, behind a k≥5 anonymity floor applied level by level: an install is counted under its city when at least 5 distinct installs share that city, otherwise under its state or province, then its country, by the same rule. Anything still below 5 collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). Each install is counted in exactly one bucket, and no published city, state, or country label ever represents fewer than 5 installs. + 地区仅以汇总形式公开,且位于逐级应用的 k≥5 匿名阈值之后:当同一城市至少有 5 个不同安装时,安装按其城市计数;否则按同一规则先改按其州或省计数,再改按其国家计数。仍低于 5 的部分会合并到单个“Other”桶中(当该合并计数本身低于 5 时则完全抑制)。每次安装只会计入一个桶,任何公开的城市、州或国家标签都绝不会代表少于 5 次安装。 src/app/pages/privacy/privacy-page.component.html 221 - - The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). - 粗略标签存储在原始事件(由 7 天保留策略删除)以及你的安装的每日汇总中;每日汇总保留 365 天,以便 /stats 按每次安装最近的地区只计数一次。该标签绝不是 IP 地址、城市或坐标;当你请求删除时,它会与你安装的其余数据一并删除;并且它只会在上述 k≥5 阈值之后公开。地理定位数据来自 DB-IP(https://db-ip.com)。 + + The /stats globe places each published label at the approximate center of that city, state, or country, taken from the same dataset — never at the location of any install. + /stats 上的地球仪会将每个公开标签放在该城市、州或国家的大致中心位置(取自同一数据集),绝不会放在任何安装的所在位置。 src/app/pages/privacy/privacy-page.component.html 222,223 + + The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, street address, or coordinates — a city name is the finest detail kept; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). + 粗略标签存储在原始事件(由 7 天保留策略删除)以及你的安装的每日汇总中;每日汇总保留 365 天,以便 /stats 按每次安装最近的地区只计数一次。该标签绝不是 IP 地址、街道地址或坐标——保留的最精细信息是城市名称;当你请求删除时,它会与你安装的其余数据一并删除;并且它只会在上述 k≥5 阈值之后公开。地理定位数据来自 DB-IP(https://db-ip.com)。 + + src/app/pages/privacy/privacy-page.component.html + 223,224 + + Retention 保留期 src/app/pages/privacy/privacy-page.component.html - 225,226 + 226,227 @@ -10719,7 +10747,7 @@ https://full-selfbrowsing.com/api/telemetry/forget原始事件保留 7 天。每日汇总(每安装每天一行,包括粗略的地区标签)保留 365 天。全局聚合(每天一行,不按安装维度)无限期保留,以便 /stats 上的历史图表保持稳定。 src/app/pages/privacy/privacy-page.component.html - 226,228 + 227,229 @@ -10727,7 +10755,7 @@ https://full-selfbrowsing.com/api/telemetry/forget如何选择退出 src/app/pages/privacy/privacy-page.component.html - 228,229 + 229,230 @@ -10735,7 +10763,7 @@ https://full-selfbrowsing.com/api/telemetry/forget打开 FSB 控制面板,滚动到"高级设置",将 发送匿名使用数据 开关关闭。更改立即生效;之后不会再从你的安装发送事件。 src/app/pages/privacy/privacy-page.component.html - 229,231 + 230,232 @@ -10743,7 +10771,7 @@ https://full-selfbrowsing.com/api/telemetry/forget如何擦除你的数据 src/app/pages/privacy/privacy-page.component.html - 231,232 + 232,233 @@ -10751,7 +10779,7 @@ https://full-selfbrowsing.com/api/telemetry/forget如要请求删除与您的安装相关的所有遥测数据行(GDPR 第 17 条),请查找您的 fsbInstallUuid:前往 Chrome DevTools → 应用程序 → 存储 → 扩展程序存储,然后发送一次 HTTP 请求: src/app/pages/privacy/privacy-page.component.html - 232,233 + 233,234 @@ -10763,7 +10791,7 @@ https://full-selfbrowsing.com/api/telemetry/forget src/app/pages/privacy/privacy-page.component.html - 233,237 + 234,238 @@ -10771,7 +10799,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合规声明 src/app/pages/privacy/privacy-page.component.html - 237,238 + 238,239 @@ -10779,7 +10807,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB 的匿名使用遥测仅用于计算在 full-selfbrowsing.com/stats 公开展示的聚合使用统计。数据从不出售、从不与第三方共享、从不用于广告,且从不用于训练任何机器学习模型。此承诺满足 Chrome Web Store 的 Limited Use 要求。 src/app/pages/privacy/privacy-page.component.html - 238,240 + 239,241 @@ -10787,7 +10815,7 @@ https://full-selfbrowsing.com/api/telemetry/forget聚合的公开指标 src/app/pages/privacy/privacy-page.component.html - 240,241 + 241,242 @@ -10795,7 +10823,7 @@ https://full-selfbrowsing.com/api/telemetry/forget我们在 /stats 发布来自此遥测管道的聚合指标。仅展示计数和总数;每安装的行从不暴露。 src/app/pages/privacy/privacy-page.component.html - 241,243 + 242,244 @@ -10803,7 +10831,7 @@ https://full-selfbrowsing.com/api/telemetry/forget同意和审计控制 src/app/pages/privacy/privacy-page.component.html - 244,245 + 245,246 @@ -10811,7 +10839,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB 的自动化策略采用选择退出模式:未列入拒绝列表的来源会继承用户可配置的全局默认设置,当前默认出厂设置为 Auto;明确的按来源策略则可将来源设置为 Off、Ask 或 Auto。审计日志仅在本地保留且有期限,控制面板也提供导出与清除功能。 src/app/pages/privacy/privacy-page.component.html - 245,247 + 246,248 @@ -10819,7 +10847,7 @@ https://full-selfbrowsing.com/api/telemetry/forget服务拒绝列表仍是硬性限制。无论全局默认值或已存储的按来源策略为何,被拒绝的来源都无法启用。 src/app/pages/privacy/privacy-page.component.html - 247,248 + 248,249 @@ -10827,7 +10855,7 @@ https://full-selfbrowsing.com/api/telemetry/forget敏感源可以运行在 Auto 下读取,但在执行前写着重新强制每个源的变异选择。 非敏感来源可选择退出或移至控制小组的同意和审计科。 src/app/pages/privacy/privacy-page.component.html - 248,249 + 249,250 @@ -10835,7 +10863,7 @@ https://full-selfbrowsing.com/api/telemetry/forget每次能力调用都会记录在经过脱敏的、仅可追加的本地审计日志中。绝不会存储任何参数、令牌、Cookie 或响应正文。 src/app/pages/privacy/privacy-page.component.html - 249,252 + 250,253 @@ -10843,7 +10871,7 @@ https://full-selfbrowsing.com/api/telemetry/forget开源 src/app/pages/privacy/privacy-page.component.html - 253,254 + 254,255 @@ -10851,7 +10879,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB在MIT下完全开源 执照 你可以对每条代码进行审核 以核实这些隐私诉求 源代码可见于GitHub. src/app/pages/privacy/privacy-page.component.html - 254,256 + 255,257 @@ -10859,7 +10887,7 @@ https://full-selfbrowsing.com/api/telemetry/forget本政策的变更 src/app/pages/privacy/privacy-page.component.html - 257,258 + 258,259 @@ -10867,7 +10895,7 @@ https://full-selfbrowsing.com/api/telemetry/forget若本政策更新,变更将体现在页面顶部的“最后更新”日期。重要变更也会在项目的 GitHub 发布说明中注明。 src/app/pages/privacy/privacy-page.component.html - 258,260 + 259,261 @@ -10875,7 +10903,7 @@ https://full-selfbrowsing.com/api/telemetry/forget联系方式 src/app/pages/privacy/privacy-page.component.html - 261,262 + 262,263 @@ -10883,7 +10911,7 @@ https://full-selfbrowsing.com/api/telemetry/forget如果您对此项隐私政策或FSB的数据处理有疑问,请在GitHub Issues上打开一个问题。 src/app/pages/privacy/privacy-page.component.html - 262,264 + 263,265 @@ -10891,7 +10919,7 @@ https://full-selfbrowsing.com/api/telemetry/forget政策历史 src/app/pages/privacy/privacy-page.component.html - 265,266 + 266,267 @@ -10899,7 +10927,7 @@ https://full-selfbrowsing.com/api/telemetry/forget下方每个条目都是所示日期当天完整的隐私政策快照。我们保留旧版本,方便您随时审计我们过去做出的承诺。 src/app/pages/privacy/privacy-page.component.html - 266,268 + 267,269 @@ -11623,7 +11651,7 @@ https://full-selfbrowsing.com/api/telemetry/forget正在驱动浏览器 src/app/pages/release-notes/release-notes-page.component.html - 176,180 + 176,179 @@ -11775,7 +11803,7 @@ https://full-selfbrowsing.com/api/telemetry/forget输入已禁用 · 该标签页归其他自动化所有 src/app/pages/release-notes/release-notes-page.component.html - 226,231 + 226,230 @@ -12351,7 +12379,7 @@ https://full-selfbrowsing.com/api/telemetry/forget元素 src/app/pages/release-notes/release-notes-page.component.html - 408,412 + 408,411 @@ -12511,7 +12539,7 @@ https://full-selfbrowsing.com/api/telemetry/forget560 项受保护 src/app/pages/release-notes/release-notes-page.component.html - 469,473 + 469,472 @@ -12671,7 +12699,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetnode 24 src/app/pages/release-notes/release-notes-page.component.html - 529,533 + 529,532 @@ -17999,7 +18027,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB - 版本说明 src/app/pages/release-notes/release-notes-page.component.ts - 88 + 86 @@ -18007,7 +18035,7 @@ https://full-selfbrowsing.com/api/telemetry/forget以图解方式呈现 FSB 的每一个版本:委派的智能体运行时、工具栏活动图标、多智能体标签页归属、MCP 桥接与能力目录,一直回溯到首个 Chrome 扩展程序原型。 src/app/pages/release-notes/release-notes-page.component.ts - 89 + 87 @@ -18179,11 +18207,11 @@ https://full-selfbrowsing.com/api/telemetry/forget - Where installs were last seen, by coarse region, over the past 365 days. - 过去 365 天内各安装最后出现的位置,按粗略地区显示。 + Where installs were last seen over the past 365 days, by city, state, or country. Place data by DB-IP. + 过去 365 天内各安装最后出现的位置,按城市、州或国家显示。地点数据来自 DB-IP。 src/app/pages/stats/stats-page.component.html - 104,106 + 104,105 @@ -18239,7 +18267,7 @@ https://full-selfbrowsing.com/api/telemetry/forget网络或剖析错误 。 src/app/pages/stats/stats-page.component.ts - 132 + 133 @@ -18247,7 +18275,7 @@ https://full-selfbrowsing.com/api/telemetry/forget星标 src/app/pages/stats/stats-page.component.ts - 139 + 140 @@ -18255,7 +18283,7 @@ https://full-selfbrowsing.com/api/telemetry/forget提交 src/app/pages/stats/stats-page.component.ts - 140 + 141 @@ -18263,7 +18291,7 @@ https://full-selfbrowsing.com/api/telemetry/forget当前活跃 src/app/pages/stats/stats-page.component.ts - 141 + 142 @@ -18271,7 +18299,7 @@ https://full-selfbrowsing.com/api/telemetry/forget令牌 src/app/pages/stats/stats-page.component.ts - 142 + 143 @@ -18279,27 +18307,7 @@ https://full-selfbrowsing.com/api/telemetry/forget热门 src/app/pages/stats/stats-page.component.ts - 143 - - - - Could not load chart library. - 无法装入图表库 。 - - src/app/pages/stats/stats-page.component.ts - 183 - - - - Could not render the selected chart. - 无法呈现所选图表。 - - src/app/pages/stats/stats-page.component.ts - 190 - - - src/app/pages/stats/stats-page.component.ts - 862 + 144 @@ -18307,7 +18315,7 @@ https://full-selfbrowsing.com/api/telemetry/forget统计数据正在预热,稍后将重试。 src/app/pages/stats/stats-page.component.ts - 232 + 233 @@ -18315,7 +18323,7 @@ https://full-selfbrowsing.com/api/telemetry/forget统计响应缺少新鲜度元数据。 src/app/pages/stats/stats-page.component.ts - 235 + 236 @@ -18323,7 +18331,7 @@ https://full-selfbrowsing.com/api/telemetry/forget最后一份可用快照已超过 24 小时。 src/app/pages/stats/stats-page.component.ts - 238 + 239 @@ -18331,7 +18339,7 @@ https://full-selfbrowsing.com/api/telemetry/forget统计响应格式有误。 src/app/pages/stats/stats-page.component.ts - 241 + 242 @@ -18339,7 +18347,7 @@ https://full-selfbrowsing.com/api/telemetry/forget统计数据仅在浏览器中可用。 src/app/pages/stats/stats-page.component.ts - 244 + 245 @@ -18347,7 +18355,7 @@ https://full-selfbrowsing.com/api/telemetry/forget代码库累计星标随时间变化 src/app/pages/stats/stats-page.component.ts - 252 + 253 @@ -18355,7 +18363,7 @@ https://full-selfbrowsing.com/api/telemetry/forget代码库累计提交随时间变化 src/app/pages/stats/stats-page.component.ts - 254 + 255 @@ -18363,7 +18371,7 @@ https://full-selfbrowsing.com/api/telemetry/forget过去 30 天的 FSB 令牌用量 src/app/pages/stats/stats-page.component.ts - 256 + 257 @@ -18371,7 +18379,7 @@ https://full-selfbrowsing.com/api/telemetry/forget已跟踪 MCP 客户端的占比 src/app/pages/stats/stats-page.component.ts - 258 + 259 @@ -18379,7 +18387,7 @@ https://full-selfbrowsing.com/api/telemetry/forget未知 src/app/pages/stats/stats-page.component.ts - 295 + 296 @@ -18387,15 +18395,15 @@ https://full-selfbrowsing.com/api/telemetry/forget其他 src/app/pages/stats/stats-page.component.ts - 296 + 297 - Globe showing where FSB installs were last seen over the past 365 days, by coarse region - 显示过去 365 天内 FSB 安装最后出现位置(按粗略地区)的地球仪 + Globe showing where FSB installs were last seen over the past 365 days, by city, state, or country + 显示过去 365 天内 FSB 安装最后出现位置(按城市、州或国家)的地球仪 src/app/pages/stats/stats-page.component.ts - 308 + 309 @@ -18403,7 +18411,7 @@ https://full-selfbrowsing.com/api/telemetry/forget恒星总数 src/app/pages/stats/stats-page.component.ts - 317 + 318 @@ -18411,7 +18419,7 @@ https://full-selfbrowsing.com/api/telemetry/forget过去7天 src/app/pages/stats/stats-page.component.ts - 318 + 319 @@ -18419,7 +18427,7 @@ https://full-selfbrowsing.com/api/telemetry/forget承付总额 src/app/pages/stats/stats-page.component.ts - 325 + 326 @@ -18427,7 +18435,7 @@ https://full-selfbrowsing.com/api/telemetry/forget过去30天 src/app/pages/stats/stats-page.component.ts - 329 + 330 @@ -18435,7 +18443,7 @@ https://full-selfbrowsing.com/api/telemetry/forget活动代理 src/app/pages/stats/stats-page.component.ts - 343 + 344 @@ -18443,7 +18451,7 @@ https://full-selfbrowsing.com/api/telemetry/forget平均值/上报用户 src/app/pages/stats/stats-page.component.ts - 347 + 348 @@ -18451,7 +18459,7 @@ https://full-selfbrowsing.com/api/telemetry/forget令牌 src/app/pages/stats/stats-page.component.ts - 357 + 358 @@ -18459,7 +18467,7 @@ https://full-selfbrowsing.com/api/telemetry/forget令牌(24 小时) src/app/pages/stats/stats-page.component.ts - 358 + 359 @@ -18467,7 +18475,7 @@ https://full-selfbrowsing.com/api/telemetry/forget跟踪客户 src/app/pages/stats/stats-page.component.ts - 364 + 365 @@ -18475,7 +18483,7 @@ https://full-selfbrowsing.com/api/telemetry/forget首位: src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18483,7 +18491,7 @@ https://full-selfbrowsing.com/api/telemetry/forget顶端客户端 src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18491,7 +18499,7 @@ https://full-selfbrowsing.com/api/telemetry/forget选择统计视图。当前视图: src/app/pages/stats/stats-page.component.ts - 402 + 403 @@ -18499,7 +18507,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB 数据 src/app/pages/stats/stats-page.component.ts - 478 + 484 @@ -18507,7 +18515,7 @@ https://full-selfbrowsing.com/api/telemetry/forget实时汇总 FSB 的采用情况、使用情况和代码仓库信号。 src/app/pages/stats/stats-page.component.ts - 486 + 492 @@ -18515,7 +18523,7 @@ https://full-selfbrowsing.com/api/telemetry/forget累积恒星 src/app/pages/stats/stats-page.component.ts - 974 + 979 @@ -18523,7 +18531,7 @@ https://full-selfbrowsing.com/api/telemetry/forget累计承付款 src/app/pages/stats/stats-page.component.ts - 995 + 1000 @@ -18531,7 +18539,7 @@ https://full-selfbrowsing.com/api/telemetry/forget令牌(过去 30 天) src/app/pages/stats/stats-page.component.ts - 1023 + 1028 @@ -18539,7 +18547,23 @@ https://full-selfbrowsing.com/api/telemetry/forget热门 MCP 客户端 src/app/pages/stats/stats-page.component.ts - 1046 + 1051 + + + + Could not load chart library. + 无法装入图表库 。 + + src/app/pages/stats/stats-page.component.ts + 1105 + + + + Could not render the selected chart. + 无法呈现所选图表。 + + src/app/pages/stats/stats-page.component.ts + 1106 diff --git a/showcase/angular/src/locale/messages.zh-TW.xlf b/showcase/angular/src/locale/messages.zh-TW.xlf index 582b118a..8599a0af 100644 --- a/showcase/angular/src/locale/messages.zh-TW.xlf +++ b/showcase/angular/src/locale/messages.zh-TW.xlf @@ -5515,7 +5515,7 @@ 最後快照: src/app/pages/dashboard/dashboard-page.component.ts - 3994 + 4014 @@ -5523,7 +5523,7 @@ 狀態: src/app/pages/dashboard/dashboard-page.component.ts - 3998 + 4018 @@ -5531,7 +5531,7 @@ 原因: src/app/pages/dashboard/dashboard-page.component.ts - 4002 + 4022 @@ -5539,7 +5539,7 @@ 的恢復 src/app/pages/dashboard/dashboard-page.component.ts - 4006 + 4026 @@ -5547,7 +5547,7 @@ 最後一幀: 前 src/app/pages/dashboard/dashboard-page.component.ts - 4012 + 4032 @@ -5555,7 +5555,7 @@ 突變: src/app/pages/dashboard/dashboard-page.component.ts - 4013 + 4033 @@ -5563,7 +5563,7 @@ 應用失敗: src/app/pages/dashboard/dashboard-page.component.ts - 4014 + 4034 @@ -5571,7 +5571,7 @@ 陳舊: src/app/pages/dashboard/dashboard-page.component.ts - 4015 + 4035 @@ -5579,7 +5579,7 @@ 無流資料 src/app/pages/dashboard/dashboard-page.component.ts - 4016 + 4036 @@ -5587,7 +5587,7 @@ 請求失敗,狀態為 src/app/pages/dashboard/dashboard-page.component.ts - 4319 + 4339 @@ -5595,7 +5595,7 @@ 約剩 分鐘 src/app/pages/dashboard/dashboard-page.component.ts - 4474 + 4494 @@ -5603,7 +5603,7 @@ 約剩 秒 src/app/pages/dashboard/dashboard-page.component.ts - 4475 + 4495 @@ -5611,7 +5611,7 @@ 執行 src/app/pages/dashboard/dashboard-page.component.ts - 4549 + 4569 @@ -5619,7 +5619,7 @@ 被使用者停止 - 為: src/app/pages/dashboard/dashboard-page.component.ts - 4554 + 4574 @@ -9542,12 +9542,32 @@ 307 + + September 2026v0.9.91 — 365-day region retention, Region (state-level) metric (full archived text) + 2026 年 9 月v0.9.91 — 地區資訊保留 365 天、地區(州級)指標(完整封存文本) + + src/app/pages/privacy/privacy-history-archive.component.html + 3,6 + + + + Archived copy of the privacy policy as it stood from September 28, 2026, before the September 29, 2026 update.TLDR: FSB runs inside your browser, and no browsing data is collected by FSB servers. AI calls go directly from your browser to the provider you choose. API keys and credentials are encrypted locally, and memory data stays on your device. If you install the optional local agent service, FSB can also be driven by an MCP client running on your own machine over a loopback connection, and by coding-agent CLIs you have already installed. The only data that ever leaves your machine for FSB is opt-out anonymous usage telemetry that powers the public /stats page. If you pair Remote Dashboard, transient live-preview frames can pass through the relay during the session, but they are not stored. Everything is open source and auditable.Data CollectionFSB operates entirely within your browser. When you initiate an automation task, the extension may inspect the active tab's DOM (Document Object Model) and, when you invoke a capability, make same-origin site API requests from your browser session.No browsing history is collected or stored beyond the current sessionDOM data and site API results are analyzed locally and discarded after each automation step unless you explicitly save them in memoryNo personal information is harvested from pages you visitSite API capabilitiesFSB's capability layer can call a site's own first-party API from the page context, using your already-authenticated browser session. These requests run locally in Chrome; first-party cookies or site auth may be attached by the browser for that target site, but FSB does not return, store, log, or send cookies, tokens, CSRF values, request bodies, or response bodies to FSB servers.Capability invocations follow FSB's consent controls and are recorded only in the redacted local audit log: origin, capability slug, method, side-effect class, consent decision, and outcome. The audit log never stores invocation arguments, request bodies, response bodies, cookies, tokens, CSRF values, or site response payloads.Chrome permissions FSB requestsTo run web automation, FSB declares the following permissions in its Chrome manifest. Each is used only for the documented purpose; nothing is sent off-device on the strength of any of them.DOM and tabs, activeTab, scripting, tabs, windows, sidePanel, and host permission <all_urls>: read and write the active tab, inject the automation content script, list and switch tabs, and render the side panelAdvanced automation, debugger: attach the Chrome DevTools Protocol for coordinate-based clicks, drag, and key-hold actions that the regular DOM API cannot perform. webNavigation: observe navigation start/finish events so automation waits for the right momentLocal storage, storage, unlimitedStorage: store your settings, credentials, payment methods, and memory in chrome.storage.local on your device. Unlimited storage lifts the default 10 MB quota so memory and session logs can grow without hitting a wallUX helpers, clipboardWrite: write copy-to-clipboard results from automation. alarms: schedule background housekeeping. offscreen: host the speech-to-text recorder in a hidden document because service workers cannot capture audio directlyLocal service, nativeMessaging: start the optional local agent service described under Local Agent Service and Native Messaging below. Nothing about the pages you visit crosses that channel. system.memory: read the total installed RAM figure only, so FSB can suggest a sensible limit on how many agents run at once. The available-memory and per-processor breakdowns are never read, and the figure never leaves the extensionMicrophone access for speech-to-text is not declared in the manifest. Chrome shows its own permission prompt the first time you use the mic button.Data StorageAll settings and data are stored locally in Chrome's extension storage, with sensitive values encrypted at rest as described in the sections below.Configuration is stored in chrome.storage.localAPI keys, credentials, and payment methods are encrypted before storage, each detailed in its own section belowSession logs are stored locally and can be cleared at any timeAnalytics data (task counts, success rates) stays on your deviceExternal ServicesFSB communicates with external AI providers only when you configure and use a hosted provider. If you use LM Studio, AI requests stay on your machine through its local OpenAI-compatible server. The choice of provider and what data is sent is under your control.Hosted API calls are made only to the provider you select (xAI, OpenAI, Anthropic, Google, or OpenRouter)LM Studio uses a local OpenAI-compatible server on your device and does not require an API keySent data includes: task description, DOM structure summary, and action contextIf you pair Remote Dashboard or use legacy Background Agents sync, an optional relay server handles WebSocket messages for that session. Live-preview frames may pass through the relay transiently, but none of the following is ever persisted on an FSB server: page content, DOM data, browsing history, screenshots, AI prompts, AI responses, cookies, tokens, or site API payloads. This is opt-in only, and the rest of this policy refers back to this list rather than restating itEach provider has their own privacy policy governing how they handle API requestsLocal Agent Service and Native MessagingFSB can be driven by an MCP client running on your own machine. That requires a small local agent service which you install yourself, and the nativeMessaging permission exists solely so the extension can start it. Chrome cannot launch a local program directly, so this is the only supported mechanism.The messaging host io.github.fullselfbrowsing.fsb_native_host is registered only by an explicit action you take, by running fsb-mcp-server install --native-host. The extension cannot install it, and its registration names one permitted extension originThe exchange is a closed schema. FSB sends a version number, an action of either wake or bootstrap, and a locally generated correlation identifier; it receives that identifier back with an outcome and a reason drawn from a fixed list. Any additional field is rejected outright and messages are capped at 4 KB. No URL, tab, page content, or DOM data exists anywhere in this schemaA successful start returns a pairing code. This is a loopback authentication secret generated on your device and stored with owner-only permissions, rotated every time the service starts. It carries no information about you or your browsingThe service listens on your machine only. The extension bridge refuses to bind any address that is not loopback, and the MCP endpoint binds 127.0.0.1 by default and whenever FSB starts itUnder its own directory the service keeps only operational records: a diagnostics log of delegated-run outcomes restricted to a fixed field list, and a process-supervision journal. Task text, environment values, binary paths, and error text never reach either of them, and entries carrying credential-shaped values are discarded rather than writtenIf the host is not installed, the feature simply does not start. Nothing is transmitted, and the rest of the extension continues to work normallyDelegated CLI AgentsFSB can hand a task to a coding-agent command-line tool you have already installed, currently Claude Code or Grok Build. FSB starts that program on your own machine; your task is never proxied through FSB servers.Your task text reaches the tool over its standard input and nowhere else. FSB checks this before every run: if the task appears in the command line, the working directory, the environment, or any file path, the run is aborted rather than started. Tasks are capped at 64 KBWhile the run is in progress the tool calls back into FSB over the loopback connection to drive your browser. Whatever it reads that way — page text, DOM snapshots, spreadsheet values, screenshots — is returned to that tool, and therefore reaches its vendor: Anthropic for Claude Code, xAI for Grok BuildThe run uses the account already signed in to that tool. FSB does not hold, read, or transmit your Anthropic or xAI credential, and any usage or charges follow that vendor's own planGrok Build runs inside a profile directory FSB keeps separate from your own. The sign-in credential stored there is written by the Grok tool itself during its own sign-in flowThe agent instructions FSB supplies state that passwords, CVV values, payment-card data, and saved credentials must never enter prompts, narration, logs, or tool argumentsReading and Filling Google SheetsFSB can read and fill a range in Google Sheets by driving the spreadsheet interface in your own already-authenticated browser session.Reading a range returns those cell values to the model you selected, exactly like any other page content FSB reads on your behalf. Only the range you asked for is read; there is no whole-spreadsheet exportSpreadsheet content is stripped out before anything is written to FSB's local session history. Only shape counts survive — how many rows, columns, and values were involved. The spreadsheet address, sheet name, cell references, and every cell value are discardedIf that filtering cannot be applied to a record for any reason, the record is dropped rather than storedRemote Dashboard and PhantomStream Live PreviewWhen you pair Remote Dashboard, FSB can show a live preview of the active browser tab using PhantomStream. The preview streams structured DOM data instead of pixels: an initial snapshot, MutationObserver diffs, scroll position, automation overlays, dialog state, media playback state, and remote-control messages over WebSocket.The relay forwards those live-preview frames only to the paired dashboard session, and nothing from the preview stream is persisted on FSB servers — see the list under External Services aboveFSB enables PhantomStream input masking (maskInputs: true), so passwords and form-control values are masked before they leave the captured pageThe dashboard viewer renders mirrored content in a scriptless sandbox and sanitizes mirrored DOM and CSS before displayImages, video, and audio are mirrored by reference in current dashboard mode. Media bytes do not cross the relay; the viewer may fetch allowed public HTTPS asset or media URLs directly, while private, internal, non-HTTPS, or otherwise blocked origins are replaced with placeholders by PhantomStream's fail-closed fetch policyNo Third-Party TrackingFSB does not include any third-party analytics, ad trackers, or cross-site fingerprinting. There are no cookies and no third-party scripts beyond the AI provider APIs you explicitly configure. The one piece of first-party data FSB sends home is the opt-out Anonymous Usage Telemetry described below, used solely to power the public /stats dashboard.API KeysYour API keys are encrypted locally using AES-GCM before being stored. They are never transmitted anywhere except to the AI provider you configured, and only as authentication headers in API requests.Keys are encrypted at rest in Chrome storageDecryption only happens in-memory when making API callsKeys are never logged, exported, or sharedAuto-PasswordsFSB includes an optional credential manager that stores login credentials encrypted on your device. Passwords are never exposed to AI models. They are filled directly into pages by the content script, bypassing the AI entirely.Credentials are encrypted at rest using AES-GCM with 256-bit keys and PBKDF2 key derivationWhen the AI analyzes a page, password field values are replaced with [hidden]. The actual password is never included in any AI promptAuto-fill is performed by the content script injecting values directly into the DOM, with no AI involvement in the credential flowThe credential list view only shows usernames and domains. Passwords are decrypted individually and only when needed for auto-fillCredentials are stored per-domain with parent domain fallback (e.g., accounts.google.com inherits from google.com)Payment MethodsFSB includes an optional payment-method vault that stores card details on your device for checkout auto-fill. Cards are treated with the same encryption and AI isolation as login credentials, and the full card number is never sent to any AI model.Card details (number, expiry, cardholder, and zip) are encrypted at rest using AES-GCM with the same vault-derived key used for credentialsWhen the AI analyzes a checkout page, any detected card-number field values are replaced with [hidden] before the prompt is built. Card numbers, CVV, and expiry are never included in any AI promptAuto-fill happens via the content script writing directly into the page's DOM fields, bypassing the AI entirelyThe list view shows only a card nickname and last-4 digits. Full numbers are decrypted in memory only at the moment of fillAn MCP client can request a payment fill via use_payment_method, but the user is shown an in-extension confirmation prompt before any card data is written into the pageCVV is never persisted unless you opt in per-card, and even then it is encrypted alongside the rest of the recordSpeech-to-TextFSB includes an optional microphone input for the prompt box. The default provider runs entirely in your browser; an optional OpenAI Whisper fallback can be enabled in settings if you want higher accuracy.Default provider: the browser's native SpeechRecognition API. Audio is processed by Chrome and never leaves your device through FSBOptional Whisper provider: when sttProvider is set to whisper and an OpenAI key is configured, recorded audio chunks are uploaded directly from your browser to OpenAI's transcription endpoint. FSB never sees or stores the audioThe microphone is only active while you are holding or have toggled the mic button. Chrome prompts for permission the first time you use it; FSB does not request microphone access in the extension manifestTranscripts are inserted into the prompt textarea only and are never logged, persisted, or transmitted outside the active AI request you choose to sendDisable speech entirely by leaving the mic button untouched, or by clearing the optional Whisper provider in Chrome extension storagePrompt Injection PreventionWeb pages can contain hidden text designed to hijack AI agents. FSB implements multi-layered defenses to ensure the AI only follows your instructions, never instructions embedded in page content.All page content is wrapped in [PAGE_CONTENT] boundary markers, and the AI is instructed to never follow instructions found within these markersA sanitization engine strips known injection patterns (e.g., "ignore previous instructions", fake system prompts, override attempts) from all page content before it reaches the AIAI-generated actions are validated before execution. Dangerous URLs (javascript:, data:) and script injection attempts are blockedOnly a strict, fixed allowlist of known tools can be executed. The AI cannot invent or call arbitrary actionsContent size is capped (500 chars per value, 15K total prompt cap) to limit payload deliveryInvisible Unicode control characters that websites embed are stripped before processingBackground Agents and Server SyncDeprecated in v0.9.45rc1. FSB's built-in Background Agents have been superseded by OpenClaw and Claude Routines, with remote control now handled by the Sync tab. The disclosures below are retained for users still running v0.9.44 or earlier; on current builds the relay server is only contacted when you pair a Sync session.If you opt into legacy Background Agents server sync or pair Remote Dashboard, a relay server facilitates communication between your extension and the dashboard.The server stores: agent definitions (name, schedule, target URL), run metrics (token count, cost, duration, success/fail status), and session pairing tokensBeyond that run metadata the server persists nothing derived from the pages you visit — see the list under External Services aboveAuthentication uses hash keys (generated locally) and session tokens that expire after 24 hoursOne-time pairing tokens expire after 60 seconds and cannot be reusedServer sync is disabled by default. You must explicitly enable it in OptionsMemory SystemFSB's memory system stores navigation patterns and site intelligence to improve automation over time.All memory data (semantic, episodic, procedural) is stored locally in chrome.storage.localNo memory data is sent to any external serverMemory can be viewed and cleared at any time from the Options dashboardSite maps and navigation patterns are domain-specific and isolated from each otherSession Replay and ScreenshotsWhen an MCP client drives FSB, the extension records what the agent did so you can review or replay the run afterwards. These records stay on your device.Runs still in progress are held in Chrome's session storage so they survive the service worker being evicted; finished runs are written to chrome.storage.local through the same history store your own automation runs useRecords are redacted before they are stored. Values under credential-shaped keys, text typed into fields that look like password, one-time-code, or card inputs, and signed or token-bearing URL parameters are all replaced. Ordinary addresses and selectors are preserved, because replay does not work without themRecorded runs are kept for 30 days by default, adjustable between 1 and 365 days, and older ones are pruned by a daily job. Recording can be switched off entirely in Advanced SettingsScreenshot images are never written into that history. Only capture metadata such as dimensions and byte size is retainedScreenshots an MCP client requests are saved by the local service as files on your own disk with owner-only permissions and deleted automatically after seven days. The image is also handed back to the requesting client, and therefore to that client's modelAnonymous Usage TelemetryFSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request.What we collectA random per-install UUID stored in chrome.storage.local under the key fsbInstallUuid. The UUID is generated locally and never tied to your identity.The name of the MCP client used (e.g. Claude Code, Cursor, Codex), drawn from a fixed allowlist.The model name used for a session (e.g. grok-4-fast, claude-opus-4), drawn from a fixed allowlist.Aggregate input/output token counts per session.The number of active FSB agents on your install (an integer count).What we do NOT collectPage URLs, hostnames, or browsing history.Prompts, instructions, task descriptions, or any natural-language text you send to your model provider.Page DOM, screenshots, page content, site API payloads, or AI responses.Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged.Names, usernames, account handles, or any free-form identity fields.Email addresses, phone numbers, or contact information.Region (state-level) metricThe server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept.Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs.The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com).RetentionRaw events are retained for 7 days. Daily rollups (one row per install per day, including the coarse region label) are retained for 365 days. Global aggregates (one row per day, no per-install dimension) are retained indefinitely so historical charts on /stats remain stable.How to opt outOpen the FSB Control Panel, scroll to Advanced Settings, and toggle Send anonymous usage data off. The change takes effect immediately; no further events will be sent from your install.How to erase your dataTo request erasure of all telemetry rows associated with your install (GDPR Article 17), look up your fsbInstallUuid in Chrome DevTools → Application → Storage → Extension storage, then send a single HTTP request:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use affirmationFSB's anonymous usage telemetry is used only to compute aggregate usage statistics displayed publicly at full-selfbrowsing.com/stats. The data is never sold, never shared with third parties, never used for advertising, and never used to train any machine-learning models. This commitment satisfies the Chrome Web Store's Limited Use requirement.Aggregated public metricsWe publish aggregated metrics derived from this telemetry pipeline at /stats. Only counts and totals are shown; no per-install row is ever exposed.Consent and Audit ControlsFSB's automation posture is opt-out: non-denylisted origins inherit a user-configurable global default that currently ships as Auto, while explicit per-origin policies can set an origin to Off, Ask, or Auto. Audit-log retention is local and bounded, with export and clear controls available from the Control Panel.The service denylist is still a hard block. Denied origins are non-enableable regardless of the global default or any stored per-origin policy.Sensitive origins can run reads under Auto, but writes re-enforce the per-origin mutating opt-in before execution. Non-sensitive origins can be opted out or moved to Ask from the Control Panel's Consent & Audit section.Every capability call is recorded in a redacted, append-only local audit log. No arguments, tokens, cookies, or response bodies are ever stored.Open SourceFSB is fully open source under the MIT License. You can audit every line of code to verify these privacy claims. The source code is available on GitHub.Changes to This PolicyIf this policy is updated, the changes will be reflected by the "Last updated" date at the top of this page. Significant changes will also be noted in the project's GitHub release notes.ContactIf you have questions about this privacy policy or FSB's data handling, please open an issue on GitHub Issues. + 此為自 2026 年 9 月 28 日起生效的隱私權政策的封存副本,內容為 2026 年 9 月 29 日更新前的版本。摘要: FSB 在你的瀏覽器內執行,FSB 伺服器不會收集任何瀏覽資料。AI 呼叫直接從你的瀏覽器傳送到你選擇的供應商。API 金鑰與憑證在本機加密,記憶資料保留在你的裝置上。如果你安裝了選用的本機代理服務,FSB 也可以由執行在你自己機器上、透過回送連線通訊的 MCP 用戶端驅動,以及由你已安裝的程式設計代理 CLIs 驅動。為 FSB 而離開你機器的唯一資料,是支撐公開頁面 /stats 的可選擇退出匿名使用情況遙測。Remote Dashboard 配對後,短暫的即時預覽影格可能在工作階段期間經過中繼,但不會被儲存。一切均為開源且可稽核。資料收集FSB 完全在你的瀏覽器內運作。當你啟動自動化工作時,擴充功能可能會檢查作用中分頁的 DOM(文件物件模型);當你呼叫某項功能時,也可能從瀏覽器工作階段向同源網站發出 API 請求。不收集或儲存當前工作階段之外的任何瀏覽歷史DOM 資料和網站 API 結果會在本機分析,並於每個自動化步驟後丟棄,除非你明確將其儲存到記憶中不從你訪問的頁面採集任何個人資訊網站 API 功能FSB 的功能層可在頁面情境中使用你已驗證的瀏覽器工作階段,呼叫網站自身的第一方 API。這些請求會在 Chrome 本機執行;瀏覽器可能會附加該目標網站的第一方 Cookie 或網站驗證資訊,但 FSB 不會將 Cookie、權杖、CSRF 值、請求本文或回應本文傳回、儲存、記錄或傳送到 FSB 伺服器。功能呼叫會遵循 FSB 的同意控制,且僅記錄在經過遮蔽的本機稽核日誌中:來源、功能代稱、方法、副作用類別、同意決定與結果。稽核日誌絕不會儲存呼叫引數、請求本文、回應本文、Cookie、權杖、CSRF 值或網站回應內容。Chrome 權限(FSB 要求)為了執行網頁自動化,FSB 在其 Chrome 資訊清單中宣告以下權限。每項僅用於其記錄的用途;不會以任何一項為依據將資料傳送至裝置外。DOM 與分頁、activeTab、scripting、tabs、windows、sidePanel 和主機權限 <all_urls>:讀寫作用中分頁、插入自動化內容指令碼、列出並切換分頁,以及顯示側邊面板進階自動化,debugger:附加 Chrome DevTools Protocol,以執行一般 DOM API 無法完成的座標點擊、拖曳與按住按鍵操作。webNavigation:監看導覽開始/完成事件,讓自動化在正確時機繼續本機儲存空間,storage、unlimitedStorage:將你的設定、認證資料、付款方式與記憶儲存在裝置上的 chrome.storage.local。無限制儲存空間會解除預設的 10 MB 配額,讓記憶和工作階段日誌可持續增長,不會觸及上限UX 輔助功能,clipboardWrite:寫入自動化的複製到剪貼簿結果。alarms:排程背景維護工作。offscreen:在隱藏文件中承載語音轉文字錄音器,因為 Service Worker 無法直接擷取音訊本機服務, nativeMessaging:啟動下方 本機代理服務與原生訊息傳遞 中描述的選用本機代理服務。你造訪的頁面資訊不會經過該通道。system.memory:僅讀取已安裝的 RAM 總量,以便 FSB 建議合理的並行代理數量上限。可用記憶體與各處理器的明細從不讀取,該數值也從不離開擴充功能語音轉文字所需的麥克風存取權限未在資訊清單中宣告。當你首次使用麥克風按鈕時,Chrome 會自行顯示權限提示。資料儲存所有設定與資料都本機儲存在 Chrome 的擴充功能儲存空間中,敏感值依下文各節所述在靜態時加密。設定儲存在 chrome.storage.local 中API 金鑰、憑證與付款方式在儲存前均已加密,各自的細節見下文對應章節工作階段日誌儲存在本機,可隨時清除分析資料(任務計數、成功率)保留在你的裝置上外部服務FSB 僅在你設定並使用託管供應商時,才與外部 AI 供應商通訊。如果你使用 LM Studio,AI 請求會透過其本機 OpenAI 相容伺服器留在本機。供應商選擇與傳送的資料完全由你掌控。託管 API 呼叫只會發往你選擇的供應商(xAI、OpenAI、Anthropic、Google 或 OpenRouter)LM Studio 在你的裝置上使用本機 OpenAI 相容伺服器,且無需 API 金鑰傳送的資料包括:工作描述、DOM 結構摘要和動作情境如果你配對 Remote Dashboard 或使用舊版 Background Agents 同步,選用的中繼伺服器會處理該工作階段的 WebSocket 訊息。即時預覽影格可能短暫經過中繼,但以下內容絕不會保存在 FSB 伺服器上:頁面內容、DOM 資料、瀏覽記錄、螢幕截圖、AI 提示、AI 回應、Cookie、權杖或網站 API 酬載。此功能僅為選擇加入,本政策其餘部分引用此清單而不再重複每個供應商都有自己的隱私政策,規定其如何處理 API 請求本機代理服務與原生訊息傳遞FSB 可由執行在你自己機器上的 MCP 用戶端驅動。這需要一個由你自行安裝的小型本機代理服務,而 nativeMessaging 權限的存在僅僅是為了讓擴充功能能夠啟動它。Chrome 無法直接啟動本機程式,因此這是唯一受支援的機制。訊息主機 io.github.fullselfbrowsing.fsb_native_host 僅透過你的明確操作註冊,即執行 fsb-mcp-server install --native-host。擴充功能無法安裝它,且其註冊僅列出一個被允許的擴充功能來源該交換採用封閉結構。FSB 傳送一個版本號、一個為 wake 或 bootstrap 的動作,以及一個本機產生的關聯識別碼;回傳的是該識別碼,附帶取自固定清單的結果與原因。任何額外欄位都會被直接拒絕,訊息上限為 4 KB。該結構中不存在任何 URL、分頁、頁面內容或 DOM 資料成功啟動後會回傳一個配對碼。它是在你的裝置上產生、以僅限擁有者的權限保存、並在每次服務啟動時輪替的回送驗證密鑰。其中不包含任何關於你或你瀏覽行為的資訊該服務僅在你的機器上接聽。擴充功能橋接拒絕繫結任何非回送位址,而 MCP 端點在預設情況下以及每當由 127.0.0.1 啟動時都繫結 FSB該服務在自己的目錄中僅保留運行記錄:一份限定於固定欄位清單的委派執行結果診斷日誌,以及一份程序監督日誌。任務文字、環境變數值、二進位路徑與錯誤文字都不會進入其中任何一個,含有疑似憑證值的項目會被丟棄而非寫入如果未安裝該主機,此功能就不會啟動。不會傳輸任何內容,擴充功能的其餘部分照常運行委派的 CLI 代理FSB 可以把任務交給你已安裝的程式設計代理命令列工具,目前為 Claude Code 或 Grok Build。FSB 會在你自己的機器上啟動該程式;你的任務絕不會經由 FSB 伺服器轉發。你的任務文字僅透過工具的標準輸入傳遞,別無他途。FSB 在每次執行前都會檢查這一點:若任務出現在命令列、工作目錄、環境變數或任何檔案路徑中,該次執行會被中止而非啟動。任務上限為 64 KB執行期間,該工具會透過回送連線回呼 FSB 以驅動你的瀏覽器。它由此讀取的一切內容——頁面文字、DOM 快照、試算表數值、螢幕截圖——都會回傳給該工具,因而也就到達其供應商:Anthropic 對應 Claude Code,xAI 對應 Grok Build該次執行使用你已在該工具中登入的帳戶。FSB 不會保存、讀取或傳輸你的 Anthropic 或 xAI 憑證,任何用量或費用均遵循該供應商自身的方案Grok Build 在 FSB 與你自己的目錄分開保管的設定檔目錄中執行。存放在那裡的登入憑證由 Grok 工具在其自身的登入流程中寫入由 FSB 提供的代理指令規定,密碼、CVV 值、付款卡資料與已儲存的憑證絕不得進入提示、敘述、日誌或工具參數讀取與填寫 Google SheetsFSB 可以在你已通過身分驗證的瀏覽器工作階段中操作試算表介面,從而讀取與填寫 Google Sheets 中的某個範圍。讀取某個範圍時,這些儲存格數值會傳送給你所選擇的模型,與 FSB 代表你讀取的任何其他頁面內容完全一樣。只會讀取你請求的範圍;不存在整表匯出在向 FSB 的本機工作階段記錄寫入任何內容之前,試算表內容都會被剝離。僅保留規模計數——涉及多少列、多少欄、多少個數值。試算表位址、工作表名稱、儲存格參照以及每一個儲存格數值都會被丟棄如果由於任何原因無法對某筆記錄套用該過濾,則該筆記錄會被丟棄而非儲存Remote Dashboard 與 PhantomStream 即時預覽配對 Remote Dashboard 時,FSB 可透過 PhantomStream 顯示作用中瀏覽器分頁的即時預覽。預覽串流傳送結構化的 DOM 資料而非像素,包括初始快照、MutationObserver 差異、捲動位置、自動化覆疊、對話方塊狀態、媒體播放狀態,以及透過 WebSocket 傳送的遠端控制訊息。中繼僅將這些即時預覽影格轉發給已配對的儀表板工作階段,預覽串流中的任何內容都不會保存在 FSB 伺服器上——參見上文 外部服務中的清單FSB 會啟用 PhantomStream 輸入遮蔽(maskInputs: true),因此密碼和表單控制項的值會在離開擷取頁面前遮蔽儀表板檢視器會在無指令碼的沙箱中呈現鏡像內容,並在顯示前清理鏡像的 DOM 和 CSS目前的儀表板模式會以參照方式鏡像圖片、影片和音訊。媒體位元組不會經過中繼站;檢視器可直接擷取允許的公開 HTTPS 資產或媒體 URL,而私人、內部、非 HTTPS 或其他遭封鎖的來源,則會由 PhantomStream 的失敗即封鎖擷取原則替換為預留位置無第三方追蹤FSB 不包含任何第三方分析、廣告追蹤器或跨站指紋識別。除你明確設定的 AI 供應商 APIs 之外,沒有 Cookie,也沒有第三方指令碼。FSB 向自身傳送的唯一第一方資料是下方描述的可選退出的匿名使用遙測,僅用於驅動公開的 /stats 儀表板。API 金鑰你的 API 金鑰 會在儲存前透過 AES-GCM 在本機加密。除了你設定的 AI 供應商之外,絕不會傳送到任何地方,且只在 API 請求的身份驗證頭中使用。Key 在 Chrome 儲存中以加密形式靜態儲存僅在 API 呼叫時在記憶體中解密Key 絕不會被記錄、匯出或共享自動密碼FSB 提供可選的認證資料管理器,將登入認證資料加密儲存到你的裝置。密碼不會暴露給 AI 模型,而是由內容指令碼直接填入頁面,完全繞過 AI。認證資料靜態儲存時會使用 AES-GCM、256 位元金鑰與 PBKDF2 金鑰衍生方式加密AI 分析頁面時,密碼欄位的值會被替換為 [hidden]。真實密碼絕不會出現在任何 AI 提示中自動填入由內容指令碼直接將值插入 DOM 完成,認證資料流程完全不會涉及 AI認證資料清單檢視僅顯示使用者名稱和網域。密碼只在自動填充需要時單獨解密認證資料按網域儲存,並具備父域回退(例如 accounts.google.com 會繼承 google.com)付款方式FSB 包含一個可選的付款方式保險庫,將卡片資訊保存在你的裝置上以便結帳時自動填入。卡片採用與登入憑證相同的加密與 AI 隔離機制,完整的卡號絕不會傳送給任何 AI 模型。卡片資訊(卡號、有效期限、持卡人與郵遞區號)使用與憑證相同的保險庫衍生金鑰,透過 AES-GCM 在靜態時加密當 AI 分析結帳頁面時,偵測到的卡號欄位值會在建立提示之前被替換為 [hidden]。卡號、CVV 和有效期限絕不會被納入任何 AI 提示之中自動填入由內容指令碼直接寫入頁面的 DOM 欄位完成,完全繞過 AI清單檢視僅顯示卡片暱稱與後 4 碼。完整卡號僅在填入的當下在記憶體中解密MCP 用戶端可透過 use_payment_method 請求執行付款自動填入,但在任何卡片資料寫入頁面之前,會在擴充套件內向使用者顯示確認提示除非你為某張卡片明確啟用,否則 CVV 永遠不會被持久保存;即使啟用,也會與紀錄的其餘部分一同加密語音轉文字FSB 在提示輸入框中包含一個可選的麥克風輸入。預設提供者完全在你的瀏覽器中執行;若你需要更高的準確性,可於設定中啟用可選的 OpenAI Whisper 後備方案。預設供應商:瀏覽器原生的 SpeechRecognition API。音訊由 Chrome 處理,不會透過 FSB 離開你的裝置可選的 Whisper 提供者:當 sttProvider 設定為 whisper 且已設定 OpenAI 金鑰時,錄製的音訊片段會從你的瀏覽器直接上傳到 OpenAI 的轉錄端點。FSB 從不檢視或保存該音訊麥克風僅在你按住或已切換開啟麥克風按鈕期間處於作用狀態。首次使用時 Chrome 會要求權限;FSB 不會在擴充套件資訊清單中要求麥克風存取權限轉錄結果僅插入到提示文字框中,絕不會在你選擇傳送的作用中 AI 請求之外被記錄、保存或傳輸透過不觸碰麥克風按鈕,或在 Chrome 擴充套件儲存中清除可選的 Whisper 提供者,即可完全停用語音功能防止提示詞注入網頁可能包含旨在劫持 AI 代理程式的隱藏文字。FSB 透過多層防禦確保 AI 只遵循你的指令,絕不執行嵌入在頁面內容中的指令。所有頁面內容都被包裹在 [PAGE_CONTENT] 邊界標記內,並指示 AI 永遠不要執行其中的指令在內容到達 AI 之前,清洗引擎會剝離已知的注入模式(例如“忽略以上指令”、偽造的系統提示、覆蓋請求)AI 產生的動作會在執行前進行驗證。危險 URL(javascript:、data:)和指令碼注入嘗試將被阻止只能執行固定且嚴格的已知工具允許清單。AI 無法自行發明或呼叫任意動作內容大小有上限(單值 500 字元,提示總上限 15K),限制承載資料投遞網站嵌入的不可見 Unicode 控制字元會在處理前被剝離背景代理程式與伺服器同步已於 v0.9.45rc1 淘汰。 FSB 內建的背景代理程式已由 OpenClaw 和 Claude Routines 取代,遠端控制現由「同步」分頁處理。下列說明保留給仍使用 v0.9.44 或更早版本的使用者;在目前版本中,只有在配對同步工作階段時才會連線至中繼伺服器。如果你選擇加入舊版背景代理程式伺服器同步,或配對 Remote Dashboard,中繼伺服器會協助擴充功能與儀表板通訊。伺服器會儲存:代理程式定義(名稱、計劃、目標 URL)、執行指標(權杖 數、成本、時長、成功/失敗狀態)、工作階段配對權杖除那些執行中繼資料之外,伺服器不會保存任何源自你所造訪頁面的內容——參見上文 外部服務中的清單身份驗證使用本機產生的雜湊金鑰,以及 24 小時後失效的工作階段權杖一次性配對權杖在 60 秒後失效,無法重複使用伺服器同步預設關閉,需要你在“選項”中明確啟用記憶系統FSB 的記憶系統會沉澱導覽模式和網站情報,讓自動化隨時間持續最佳化。所有記憶資料(語義、情景、過程)都儲存在 chrome.storage.local 中記憶資料不會傳送到任何外部伺服器你可在選項控制台中隨時檢視並清除記憶網站地圖和導覽模式按網域隔離,互不影響工作階段重播與螢幕截圖當 MCP 用戶端驅動 FSB 時,擴充功能會記錄代理的操作,以便你之後查看或重播該次執行。這些記錄保留在你的裝置上。仍在進行中的執行保存在 Chrome 的工作階段儲存空間中,以便在 service worker 被回收後依然留存;已完成的執行則透過 chrome.storage.local 寫入與你自己的自動化執行相同的記錄儲存空間記錄在儲存前會先經過去識別化。疑似憑證鍵下的數值、輸入到形似密碼、一次性驗證碼或金融卡欄位中的文字,以及帶簽章或攜帶權杖的 URL 參數,都會被取代。一般位址與選擇器會保留,因為沒有它們重播就無法進行已記錄的執行預設保留 30 天,可在 1 至 365 天之間調整,較舊的記錄由每日工作清理。可在進階設定中完全關閉記錄功能螢幕截圖影像絕不會寫入該記錄。僅保留尺寸、位元組大小等擷取中繼資料由 MCP 用戶端請求的螢幕截圖,會由本機服務以僅限擁有者的權限保存為你自己磁碟上的檔案,並在七天後自動刪除。該影像同時也會回傳給發起請求的用戶端,因而也會到達該用戶端的模型匿名使用遙測FSB v0.9.69 引入了一個可選擇退出的匿名使用情況遙測管道,讓專案可以發布彙總的採用資料(參見 /stats),且絕不觸及你瀏覽的頁面。唯一的位置訊號是伺服器在接收時從你的請求 IP 衍生出的粗略國家/州標籤(絕不來自頁面內容),且僅以彙總形式公開——參見下方的 地區(州級)指標。遙測預設開啟,但可透過單一開關停用,並且可應請求刪除每次安裝的資料。我們收集的內容每次安裝隨機產生的 UUID,儲存於 chrome.storage.local 的鍵 fsbInstallUuid 之下。UUID 於本機產生,永遠不會與你的身分關聯。所使用的 MCP 用戶端名稱(例如 Claude Code、Cursor、Codex),取自固定的允許清單。工作階段中使用的模型名稱(例如 grok-4-fast、claude-opus-4),取自固定的允許清單。每個工作階段的彙總輸入/輸出權杖數量。你的安裝上活躍的 FSB 代理數量(一個整數值)。我們不收集的內容頁面 URLs、主機名稱或瀏覽歷史紀錄。提示、指令、工作描述,或你傳送給模型供應商的任何自然語言文字。頁面 DOM、螢幕擷取畫面、頁面內容、網站 API 承載資料或 AI 回應。明文 IP 位址。伺服器僅出於三個目的暫時且內聯地使用請求 IP——用於限流的每日輪換加鹽雜湊、衍生粗略的國家/州標籤(見下文),以及記錄該位址是 IPv4 還是 IPv6——隨後立即將其丟棄。明文 IP 絕不會被儲存或記錄。姓名、使用者名稱、帳號代號,或任何自由文字身分欄位。電子郵件地址、電話號碼,或聯絡資訊。地區(州級)指標伺服器在接收時,會從請求的 IP 位址衍生粗略的國家與美國州(行政區)標籤,使用自行託管的 DB-IP IP-to-City Lite 資料集及自有查詢機制;並非即時第三方地理定位服務,也絕不使用 MaxMind。明文 IP 會在處理時直接使用後立即丟棄,只保留衍生標籤與位址類型(IPv4 或 IPv6)。地區僅以彙總形式公開,且位於 k≥5 的匿名閾值之後:不同安裝數少於 5 的任何州都會合併到單一「Other」桶中(當該合併計數本身低於 5 時則完全抑制)。任何公開的州標籤都絕不會代表少於 5 次安裝。粗略標籤儲存在原始事件(由 7 天保留策略刪除)以及你的安裝的每日彙總中;每日彙總保留 365 天,讓 /stats 依每次安裝最近的地區只計算一次。該標籤絕不是 IP 位址、城市或座標;當你請求刪除時,它會與你安裝的其餘資料一併刪除;而且只會在上述 k≥5 閾值之後公開。地理定位資料來自 DB-IP(https://db-ip.com)。保留期原始事件保留 7 天。每日彙總(每安裝每天一列,包括粗略的地區標籤)保留 365 天。全域聚合(每天一列,無按安裝維度)無限期保留,以便 /stats 上的歷史圖表保持穩定。如何選擇退出開啟 FSB 控制面板,捲動至「進階設定」,將 傳送匿名使用資料 開關關閉。變更立即生效;之後不會再從你的安裝傳送事件。如何抹除你的資料如要請求刪除與你的安裝相關的所有遙測資料列(GDPR 第 17 條),請查詢你的 fsbInstallUuid:前往 Chrome DevTools → 應用程式 → 儲存空間 → 擴充功能儲存空間,然後傳送一次 HTTP 請求:curl -X POST -H "Content-Type: application/json" \ +-d '"install_uuid":"<your-uuid>"' \ +https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合規聲明FSB 的匿名使用遙測僅用於計算在 full-selfbrowsing.com/stats 公開展示的聚合使用統計。資料從不出售、從不與第三方共享、從不用於廣告,且從不用於訓練任何機器學習模型。此承諾滿足 Chrome Web Store 的 Limited Use 要求。聚合的公開指標我們在 /stats 發布來自此遙測管線的聚合指標。僅展示計數和總數;每安裝的列從不公開。同意與稽核控制FSB 的自動化立場採選擇退出制:不在拒絕清單中的來源會繼承使用者可設定的全域預設值,目前出廠值為 Auto;明確的個別來源原則則可將來源設為 Off、Ask 或 Auto。稽核日誌會在本機保留且有期限,控制面板也提供匯出與清除功能。服務拒絕清單仍是強制封鎖。無論全域預設值或已儲存的個別來源原則為何,遭拒絕的來源都無法啟用。敏感來源可在 Auto 模式下執行讀取,但寫入前會再次強制檢查個別來源的變更操作加入設定。非敏感來源可在控制面板的「同意與稽核」區段選擇退出,或改為 Ask。每次能力呼叫都會記錄於經過遮蔽、僅可附加的本機稽核日誌中。絕不會儲存任何引數、權杖、Cookie 或回應內容。開源FSB 完全採用 MIT 授權條款開放原始碼。你可以稽核每一行程式碼,驗證這些隱私權聲明。原始碼可在 GitHub 取得。本政策的變更若本政策更新,變更將體現在頁面頂部的“最後更新”日期。重要變更也會在專案的 GitHub 釋出說明中註明。聯絡方式如果你對本隱私權政策或 FSB 的資料處理方式有任何疑問,請在 GitHub Issues 提出問題。 + + src/app/pages/privacy/privacy-history-archive.component.html + 7,238 + + August 2026v0.9.91 — Local Agent Service and Native Messaging, Delegated CLI Agents, Google Sheets, Session Replay and Screenshots (full archived text) 2026 年 8 月v0.9.91 — 本機代理服務與原生訊息傳遞、委派的 CLI 代理、Google Sheets、工作階段重播與螢幕截圖(完整封存文本) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 242,245 @@ -9559,7 +9579,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合規聲明FSB 的匿名使用遙測僅用於計算在 full-selfbrowsing.com/stats 公開展示的聚合使用統計。資料從不出售、從不與第三方共享、從不用於廣告,且從不用於訓練任何機器學習模型。此承諾滿足 Chrome Web Store 的 Limited Use 要求。聚合的公開指標我們在 /stats 發布來自此遙測管線的聚合指標。僅展示計數和總數;每安裝的列從不公開。同意與稽核控制FSB 的自動化立場採選擇退出制:不在拒絕清單中的來源會繼承使用者可設定的全域預設值,目前出廠值為 Auto;明確的個別來源原則則可將來源設為 Off、Ask 或 Auto。稽核日誌會在本機保留且有期限,控制面板也提供匯出與清除功能。服務拒絕清單仍是強制封鎖。無論全域預設值或已儲存的個別來源原則為何,遭拒絕的來源都無法啟用。敏感來源可在 Auto 模式下執行讀取,但寫入前會再次強制檢查個別來源的變更操作加入設定。非敏感來源可在控制面板的「同意與稽核」區段選擇退出,或改為 Ask。每次能力呼叫都會記錄於經過遮蔽、僅可附加的本機稽核日誌中。絕不會儲存任何引數、權杖、Cookie 或回應內容。開源FSB 完全採用 MIT 授權條款開放原始碼。你可以稽核每一行程式碼,驗證這些隱私權聲明。原始碼可在 GitHub 取得。本政策的變更若本政策更新,變更將體現在頁面頂部的“最後更新”日期。重要變更也會在專案的 GitHub 釋出說明中註明。聯絡方式如果你對本隱私權政策或 FSB 的資料處理方式有任何疑問,請在 GitHub Issues 提出問題。 src/app/pages/privacy/privacy-history-archive.component.html - 7,238 + 246,477 @@ -9567,7 +9587,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026 年 5 月 – 7 月v0.9.90 — 網站 API 能力、Remote Dashboard 與 PhantomStream、付款方式、語音轉文字、地區指標(完整封存文本) src/app/pages/privacy/privacy-history-archive.component.html - 3,6 + 481,484 @@ -9579,7 +9599,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合規聲明FSB的匿名使用情況遙測只用於計算公開顯示在 full-selfbrowsing.com/stats的彙總使用統計。資料絕不出售、絕不與第三方分享、絕不用於廣告,也絕不用於訓練任何機器學習模型。此承諾符合 Chrome Web Store的 Limited Use 要求。彙總公開指標我們在 /stats發布由此遙測管線產生的彙總指標。只會顯示計數與總數,絕不公開個別安裝的資料列。同意與稽核控制FSB的自動化立場採選擇退出制:不在拒絕清單中的來源會繼承使用者可設定的全域預設值,目前出廠值為 Auto;明確的個別來源原則則可將來源設為 Off、Ask 或 Auto。稽核日誌會在本機保留且有期限,控制面板也提供匯出與清除功能。服務拒絕清單仍是強制封鎖。無論全域預設值或已儲存的個別來源原則為何,遭拒絕的來源都無法啟用。敏感來源可在 Auto 模式下執行讀取,但寫入前會再次強制檢查個別來源的變更操作加入設定。非敏感來源可在控制面板的「同意與稽核」區段選擇退出,或改為 Ask。每次功能呼叫都會記錄在經過遮蔽且僅能附加的本機稽核日誌中。絕不會儲存任何引數、權杖、Cookie 或回應本文。開放原始碼FSB 完全採用 MIT 授權條款開放原始碼。你可以稽核每一行程式碼,驗證這些隱私權聲明。原始碼可在 GitHub。本政策的變更如果本政策更新,變更會反映在頁面頂端的「上次更新」日期。重大變更也會記載於專案的 GitHub 版本資訊中。聯絡我們如果你對本隱私權政策或 FSB的資料處理方式有任何疑問,請在 GitHub Issues。 src/app/pages/privacy/privacy-history-archive.component.html - 7,198 + 485,676 @@ -9587,7 +9607,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026 年 3 月v9.0.2 — 背景代理程式、記憶系統、伺服器同步、匿名使用情況遙測、法律立場(完整封存內容) src/app/pages/privacy/privacy-history-archive.component.html - 202,205 + 680,683 @@ -9599,7 +9619,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合規聲明FSB 的匿名使用情況遙測只用於計算公開顯示在 full-selfbrowsing.com/stats 的彙總使用統計。資料絕不出售、絕不與第三方分享、絕不用於廣告,也絕不用於訓練任何機器學習模型。此承諾符合 Chrome Web Store 的 Limited Use 要求。彙總公開指標我們在 /stats 發布由此遙測管線產生的彙總指標。只會顯示計數與總數,絕不公開個別安裝的資料列。法律立場與同意模式FSB 的自動化立場、稽核日誌保留方式,以及個別來源同意模式,均以明確的產品控制項呈現。FSB 在你明確允許前,不會對任何來源執行動作;自動讀取權限絕不代表寫入權限,保守的服務拒絕清單也會完全封鎖敏感類別(金融與政府服務)的自動化。個別來源同意預設為關閉。Auto 與寫入(變更)權限是分開的明確加入選項,可從控制面板的「同意與稽核」區段管理。每次功能呼叫都會記錄在經過遮蔽且僅能附加的本機稽核日誌中。絕不會儲存任何引數、權杖、Cookie 或回應本文。服務拒絕清單會讓敏感來源無法啟用;此限制由功能閘門強制執行,而非僅存在於介面中。開放原始碼FSB 完全採用 MIT 授權條款開放原始碼。你可以稽核每一行程式碼,驗證這些隱私權聲明。原始碼可在 GitHub 取得。本政策的變更如果本政策更新,變更會反映在頁面頂端的「上次更新」日期。重大變更也會記載於專案的 GitHub 版本資訊中。聯絡方式如果你對本隱私權政策或 FSB 的資料處理方式有任何疑問,請在 GitHub Issues 提出問題。 src/app/pages/privacy/privacy-history-archive.component.html - 206,345 + 684,823 @@ -9607,7 +9627,7 @@ https://full-selfbrowsing.com/api/telemetry/forget2026 年 2 月v0.9 — 初版隱私權政策(完整封存內容) src/app/pages/privacy/privacy-history-archive.component.html - 349,352 + 827,830 @@ -9615,7 +9635,7 @@ https://full-selfbrowsing.com/api/telemetry/forget此為 2026 年 2 月初版隱私權政策的封存副本。此快照重現自原始靜態展示頁面,已移除頁面框架並保留政策內容。資料收集FSB 完全在你的瀏覽器內運作。擴充功能只會在你啟動自動化工作時,存取目前作用中分頁的 DOM(文件物件模型)。不會收集或儲存目前工作階段以外的瀏覽記錄DOM 資料會在本機分析,並於每個自動化步驟後丟棄不會從你造訪的頁面擷取個人資訊資料儲存所有設定與資料皆儲存在 Chrome 的擴充功能儲存空間中。FSB 使用 AES-GCM 加密 API 金鑰等敏感資料。設定儲存在 chrome.storage.localAPI 金鑰在儲存前會使用 AES-GCM工作階段日誌儲存在本機,可隨時清除分析資料(工作數量、成功率)會保留在你的裝置上外部服務FSB 只會在你設定並使用外部 AI 供應商時與其通訊。你可以自行控制所選供應商及傳送的資料。API 呼叫只會傳送到你選擇的供應商(xAI、OpenAI、Anthropic 或 Google)傳送的資料包括:工作描述、DOM 結構摘要和動作情境不會將資料傳送到 FSB 伺服器,因為根本不存在這類伺服器每個供應商都有自己的隱私權政策,規範其處理 API 請求的方式不追蹤FSB 不包含任何分析、遙測或追蹤服務。除了你明確設定的 AI 供應商 API,不會使用 Cookie、瀏覽器指紋或第三方指令碼。API 金鑰你的 API 金鑰會在儲存前於本機使用 AES-GCM 加密。除了你設定的 AI 供應商外,絕不會傳送到其他地方,且只會作為 API 請求中的驗證標頭。金鑰在 Chrome 儲存空間中以加密形式靜態儲存只有在發出 API 呼叫時才會在記憶體中解密金鑰絕不會被記錄、匯出或分享自動密碼測試版FSB 提供選用的認證資料管理員,將登入認證資料加密儲存在你的裝置上。密碼絕不會暴露給 AI 模型,而是由內容指令碼直接填入頁面,完全不經過 AI。認證資料靜態儲存時會使用 AES-GCM、256 位元金鑰與 PBKDF2 金鑰衍生方式加密AI 分析頁面時,密碼欄位的值會替換為 [hidden];實際密碼絕不會包含在任何 AI 提示中自動填入由內容指令碼直接將值插入 DOM 完成,認證資料流程完全不會涉及 AI認證資料清單只會顯示使用者名稱與網域;密碼會個別解密,且只在自動填入需要時進行認證資料會依網域儲存,並以父網域作為備援(例如 accounts.google.com 繼承自 google.com)提示注入防護網頁可能包含企圖劫持 AI 代理程式的隱藏文字。FSB 採用多層防護,確保 AI 只遵循你的指示,絕不遵循嵌入頁面內容中的指示。所有頁面內容都會包在 [PAGE_CONTENT] 邊界標記內,並指示 AI 絕不遵循這些標記中的指示頁面內容傳送給 AI 前,清理引擎會移除已知的注入模式(例如「忽略先前的指示」、偽造的系統提示及覆寫嘗試)AI 產生的動作會在執行前驗證;危險 URL(javascript:、data:)和指令碼注入嘗試都會遭到封鎖只能執行嚴格允許清單中的 30 多項已知工具;AI 無法自行發明或呼叫任意動作內容大小設有上限(每個值 500 個字元,提示總計上限 15K),以限制承載內容的傳遞網站嵌入的不可見 Unicode 控制字元會在處理前移除開放原始碼FSB 完全採用 MIT 授權條款開放原始碼。你可以稽核每一行程式碼,驗證這些隱私權聲明。原始碼可在 GitHub 取得。在 GitHub 上檢視原始碼摘要FSB 會在本機處理資料、加密敏感資訊,絕不向 AI 模型暴露密碼,防範提示注入攻擊,只與你選擇的 AI 供應商通訊,不含任何追蹤功能,並以開放原始碼形式提供完整稽核能力。 src/app/pages/privacy/privacy-history-archive.component.html - 353,420 + 831,898 @@ -9635,8 +9655,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Last updated: September 2026 - 最後更新:2026 年 9 月 + Last updated: September 29, 2026 + 最後更新:2026 年 9 月 29 日 src/app/pages/privacy/privacy-page.component.html 6,9 @@ -10563,8 +10583,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse country/state label the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (state-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. - FSB v0.9.69 引入了一個可選擇退出的匿名使用情況遙測管道,讓專案可以發布彙總的採用資料(參見 /stats),且絕不觸及你瀏覽的頁面。唯一的位置訊號是伺服器在接收時從你的請求 IP 衍生出的粗略國家/州標籤(絕不來自頁面內容),且僅以彙總形式公開——參見下方的 地區(州級)指標。遙測預設開啟,但可透過單一開關停用,並且可應請求刪除每次安裝的資料。 + FSB v0.9.69 introduced an opt-out anonymous usage telemetry pipeline so the project can publish aggregate adoption numbers (see /stats) without ever touching the pages you browse. The only location signal is a coarse place label — city, state or province, and country — that the server derives from your request IP at ingest (never from page content), published only in aggregate — see Region (city-level) metric below. Telemetry is on by default but can be disabled with a single toggle, and the per-install data can be erased on request. + FSB v0.9.69 引入了一個可選擇退出的匿名使用情況遙測管道,讓專案可以發布彙總的採用資料(參見 /stats),且絕不觸及你瀏覽的頁面。唯一的位置訊號是一個粗略的地點標籤(城市、州或省,以及國家),由伺服器在接收時從你的請求 IP 衍生而來(絕不來自頁面內容),且僅以彙總形式公開——參見下方的 地區(城市級)指標。遙測預設開啟,但可透過單一開關停用,並且可應請求刪除每次安裝的資料。 src/app/pages/privacy/privacy-page.component.html 197,199 @@ -10651,8 +10671,8 @@ https://full-selfbrowsing.com/api/telemetry/forget - Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse country/state label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. - 明文 IP 位址。伺服器僅出於三個目的暫時且內聯地使用請求 IP——用於限流的每日輪換加鹽雜湊、衍生粗略的國家/州標籤(見下文),以及記錄該位址是 IPv4 還是 IPv6——隨後立即將其丟棄。明文 IP 絕不會被儲存或記錄。 + Plaintext IP addresses. The server uses the request IP transiently and inline for exactly three purposes — a daily-rotating-salt hash for rate limiting, deriving a coarse city, state, and country label (see below), and noting whether the address is IPv4 or IPv6 — then discards it immediately. The plaintext IP is never stored or logged. + 明文 IP 位址。伺服器僅出於三個目的暫時且內聯地使用請求 IP——用於限流的每日輪換加鹽雜湊、衍生粗略的城市/州/國家標籤(見下文),以及記錄該位址是 IPv4 還是 IPv6——隨後立即將其丟棄。明文 IP 絕不會被儲存或記錄。 src/app/pages/privacy/privacy-page.component.html 213,214 @@ -10675,43 +10695,51 @@ https://full-selfbrowsing.com/api/telemetry/forget - Region (state-level) metric - 地區(州級)指標 + Region (city-level) metric + 地區(城市級)指標 src/app/pages/privacy/privacy-page.component.html 218,220 - The server derives a coarse country and US-state (subdivision) label from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. - 伺服器在接收時,會從請求的 IP 位址衍生粗略的國家與美國州(行政區)標籤,使用自行託管的 DB-IP IP-to-City Lite 資料集及自有查詢機制;並非即時第三方地理定位服務,也絕不使用 MaxMind。明文 IP 會在處理時直接使用後立即丟棄,只保留衍生標籤與位址類型(IPv4 或 IPv6)。 + The server derives a coarse place label — country, state or province, and city — from your request IP at ingest, using a self-hosted DB-IPIP-to-City Lite dataset and our own lookup — not a live third-party geolocation service, and never MaxMind. The plaintext IP is consumed inline and discarded; only the derived label and the address family (IPv4 or IPv6) are kept. + 伺服器在接收時,會從請求的 IP 位址衍生粗略的地點標籤(國家、州或省,以及城市),使用自行託管的 DB-IP IP-to-City Lite 資料集及自有查詢機制;並非即時第三方地理定位服務,也絕不使用 MaxMind。明文 IP 會在處理時直接使用後立即丟棄,只保留衍生標籤與位址類型(IPv4 或 IPv6)。 src/app/pages/privacy/privacy-page.component.html 220,221 - Region is published only in aggregate, behind a k≥5 anonymity floor: any state with fewer than 5 distinct installs collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). No published state label ever represents fewer than 5 installs. - 地區僅以彙總形式公開,且位於 k≥5 的匿名閾值之後:不同安裝數少於 5 的任何州都會合併到單一「Other」桶中(當該合併計數本身低於 5 時則完全抑制)。任何公開的州標籤都絕不會代表少於 5 次安裝。 + Region is published only in aggregate, behind a k≥5 anonymity floor applied level by level: an install is counted under its city when at least 5 distinct installs share that city, otherwise under its state or province, then its country, by the same rule. Anything still below 5 collapses into a single “Other” bucket (suppressed entirely when even that combined count is below 5). Each install is counted in exactly one bucket, and no published city, state, or country label ever represents fewer than 5 installs. + 地區僅以彙總形式公開,且位於逐級套用的 k≥5 匿名閾值之後:當同一城市至少有 5 個不同安裝時,安裝依其城市計算;否則依同一規則先改依其州或省計算,再改依其國家計算。仍低於 5 的部分會合併到單一「Other」桶中(當該合併計數本身低於 5 時則完全抑制)。每次安裝只會計入一個桶,任何公開的城市、州或國家標籤都絕不會代表少於 5 次安裝。 src/app/pages/privacy/privacy-page.component.html 221 - - The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, city, or coordinates; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). - 粗略標籤儲存在原始事件(由 7 天保留策略刪除)以及你的安裝的每日彙總中;每日彙總保留 365 天,讓 /stats 依每次安裝最近的地區只計算一次。該標籤絕不是 IP 位址、城市或座標;當你請求刪除時,它會與你安裝的其餘資料一併刪除;而且只會在上述 k≥5 閾值之後公開。地理定位資料來自 DB-IP(https://db-ip.com)。 + + The /stats globe places each published label at the approximate center of that city, state, or country, taken from the same dataset — never at the location of any install. + /stats 上的地球儀會將每個公開標籤放在該城市、州或國家的大致中心位置(取自同一資料集),絕不會放在任何安裝的所在位置。 src/app/pages/privacy/privacy-page.component.html 222,223 + + The coarse label is stored on the raw event (dropped by the 7-day retention) and on your install's daily rollup, which is kept for 365 days so /stats can count each install once by its most recent region. It is never an IP address, street address, or coordinates — a city name is the finest detail kept; it is deleted with the rest of your per-install data when you request erasure; and it is only published behind the k≥5 floor above. Geolocation data is by DB-IP (https://db-ip.com). + 粗略標籤儲存在原始事件(由 7 天保留策略刪除)以及你的安裝的每日彙總中;每日彙總保留 365 天,讓 /stats 依每次安裝最近的地區只計算一次。該標籤絕不是 IP 位址、街道地址或座標——保留的最精細資訊是城市名稱;當你請求刪除時,它會與你安裝的其餘資料一併刪除;而且只會在上述 k≥5 閾值之後公開。地理定位資料來自 DB-IP(https://db-ip.com)。 + + src/app/pages/privacy/privacy-page.component.html + 223,224 + + Retention 保留期 src/app/pages/privacy/privacy-page.component.html - 225,226 + 226,227 @@ -10719,7 +10747,7 @@ https://full-selfbrowsing.com/api/telemetry/forget原始事件保留 7 天。每日彙總(每安裝每天一列,包括粗略的地區標籤)保留 365 天。全域聚合(每天一列,無按安裝維度)無限期保留,以便 /stats 上的歷史圖表保持穩定。 src/app/pages/privacy/privacy-page.component.html - 226,228 + 227,229 @@ -10727,7 +10755,7 @@ https://full-selfbrowsing.com/api/telemetry/forget如何選擇退出 src/app/pages/privacy/privacy-page.component.html - 228,229 + 229,230 @@ -10735,7 +10763,7 @@ https://full-selfbrowsing.com/api/telemetry/forget開啟 FSB 控制面板,捲動至「進階設定」,將 傳送匿名使用資料 開關關閉。變更立即生效;之後不會再從你的安裝傳送事件。 src/app/pages/privacy/privacy-page.component.html - 229,231 + 230,232 @@ -10743,7 +10771,7 @@ https://full-selfbrowsing.com/api/telemetry/forget如何抹除你的資料 src/app/pages/privacy/privacy-page.component.html - 231,232 + 232,233 @@ -10751,7 +10779,7 @@ https://full-selfbrowsing.com/api/telemetry/forget如要請求刪除與你的安裝相關的所有遙測資料列(GDPR 第 17 條),請查詢你的 fsbInstallUuid:前往 Chrome DevTools → 應用程式 → 儲存空間 → 擴充功能儲存空間,然後傳送一次 HTTP 請求: src/app/pages/privacy/privacy-page.component.html - 232,233 + 233,234 @@ -10763,7 +10791,7 @@ https://full-selfbrowsing.com/api/telemetry/forget src/app/pages/privacy/privacy-page.component.html - 233,237 + 234,238 @@ -10771,7 +10799,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetLimited Use 合規聲明 src/app/pages/privacy/privacy-page.component.html - 237,238 + 238,239 @@ -10779,7 +10807,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB 的匿名使用遙測僅用於計算在 full-selfbrowsing.com/stats 公開展示的聚合使用統計。資料從不出售、從不與第三方共享、從不用於廣告,且從不用於訓練任何機器學習模型。此承諾滿足 Chrome Web Store 的 Limited Use 要求。 src/app/pages/privacy/privacy-page.component.html - 238,240 + 239,241 @@ -10787,7 +10815,7 @@ https://full-selfbrowsing.com/api/telemetry/forget聚合的公開指標 src/app/pages/privacy/privacy-page.component.html - 240,241 + 241,242 @@ -10795,7 +10823,7 @@ https://full-selfbrowsing.com/api/telemetry/forget我們在 /stats 發布來自此遙測管線的聚合指標。僅展示計數和總數;每安裝的列從不公開。 src/app/pages/privacy/privacy-page.component.html - 241,243 + 242,244 @@ -10803,7 +10831,7 @@ https://full-selfbrowsing.com/api/telemetry/forget同意與稽核控制 src/app/pages/privacy/privacy-page.component.html - 244,245 + 245,246 @@ -10811,7 +10839,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB 的自動化立場採選擇退出制:不在拒絕清單中的來源會繼承使用者可設定的全域預設值,目前出廠值為 Auto;明確的個別來源原則則可將來源設為 Off、Ask 或 Auto。稽核日誌會在本機保留且有期限,控制面板也提供匯出與清除功能。 src/app/pages/privacy/privacy-page.component.html - 245,247 + 246,248 @@ -10819,7 +10847,7 @@ https://full-selfbrowsing.com/api/telemetry/forget服務拒絕清單仍是強制封鎖。無論全域預設值或已儲存的個別來源原則為何,遭拒絕的來源都無法啟用。 src/app/pages/privacy/privacy-page.component.html - 247,248 + 248,249 @@ -10827,7 +10855,7 @@ https://full-selfbrowsing.com/api/telemetry/forget敏感來源可在 Auto 模式下執行讀取,但寫入前會再次強制檢查個別來源的變更操作加入設定。非敏感來源可在控制面板的「同意與稽核」區段選擇退出,或改為 Ask。 src/app/pages/privacy/privacy-page.component.html - 248,249 + 249,250 @@ -10835,7 +10863,7 @@ https://full-selfbrowsing.com/api/telemetry/forget每次能力呼叫都會記錄於經過遮蔽、僅可附加的本機稽核日誌中。絕不會儲存任何引數、權杖、Cookie 或回應內容。 src/app/pages/privacy/privacy-page.component.html - 249,252 + 250,253 @@ -10843,7 +10871,7 @@ https://full-selfbrowsing.com/api/telemetry/forget開源 src/app/pages/privacy/privacy-page.component.html - 253,254 + 254,255 @@ -10851,7 +10879,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB 完全採用 MIT 授權條款開放原始碼。你可以稽核每一行程式碼,驗證這些隱私權聲明。原始碼可在 GitHub 取得。 src/app/pages/privacy/privacy-page.component.html - 254,256 + 255,257 @@ -10859,7 +10887,7 @@ https://full-selfbrowsing.com/api/telemetry/forget本政策的變更 src/app/pages/privacy/privacy-page.component.html - 257,258 + 258,259 @@ -10867,7 +10895,7 @@ https://full-selfbrowsing.com/api/telemetry/forget若本政策更新,變更將體現在頁面頂部的“最後更新”日期。重要變更也會在專案的 GitHub 釋出說明中註明。 src/app/pages/privacy/privacy-page.component.html - 258,260 + 259,261 @@ -10875,7 +10903,7 @@ https://full-selfbrowsing.com/api/telemetry/forget聯絡方式 src/app/pages/privacy/privacy-page.component.html - 261,262 + 262,263 @@ -10883,7 +10911,7 @@ https://full-selfbrowsing.com/api/telemetry/forget如果你對本隱私權政策或 FSB 的資料處理方式有任何疑問,請在 GitHub Issues 提出問題。 src/app/pages/privacy/privacy-page.component.html - 262,264 + 263,265 @@ -10891,7 +10919,7 @@ https://full-selfbrowsing.com/api/telemetry/forget政策歷史 src/app/pages/privacy/privacy-page.component.html - 265,266 + 266,267 @@ -10899,7 +10927,7 @@ https://full-selfbrowsing.com/api/telemetry/forget下方每個項目都是該日期當時完整的隱私權政策快照。我們保留舊版本,方便你隨時稽核我們過去做出的承諾。 src/app/pages/privacy/privacy-page.component.html - 266,268 + 267,269 @@ -11623,7 +11651,7 @@ https://full-selfbrowsing.com/api/telemetry/forget正在驅動瀏覽器 src/app/pages/release-notes/release-notes-page.component.html - 176,180 + 176,179 @@ -11775,7 +11803,7 @@ https://full-selfbrowsing.com/api/telemetry/forget輸入已停用 · 該分頁歸其他自動化所有 src/app/pages/release-notes/release-notes-page.component.html - 226,231 + 226,230 @@ -12351,7 +12379,7 @@ https://full-selfbrowsing.com/api/telemetry/forget元素 src/app/pages/release-notes/release-notes-page.component.html - 408,412 + 408,411 @@ -12511,7 +12539,7 @@ https://full-selfbrowsing.com/api/telemetry/forget560 項受保護 src/app/pages/release-notes/release-notes-page.component.html - 469,473 + 469,472 @@ -12671,7 +12699,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetnode 24 src/app/pages/release-notes/release-notes-page.component.html - 529,533 + 529,532 @@ -17999,7 +18027,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB - 版本說明 src/app/pages/release-notes/release-notes-page.component.ts - 88 + 86 @@ -18007,7 +18035,7 @@ https://full-selfbrowsing.com/api/telemetry/forget以圖解方式呈現 FSB 的每一個版本:委派的代理執行環境、工具列活動圖示、多代理分頁擁有權、MCP 橋接與能力目錄,一路回溯到首個 Chrome 擴充功能原型。 src/app/pages/release-notes/release-notes-page.component.ts - 89 + 87 @@ -18179,11 +18207,11 @@ https://full-selfbrowsing.com/api/telemetry/forget - Where installs were last seen, by coarse region, over the past 365 days. - 過去 365 天內各安裝最後出現的位置,依粗略地區顯示。 + Where installs were last seen over the past 365 days, by city, state, or country. Place data by DB-IP. + 過去 365 天內各安裝最後出現的位置,依城市、州或國家顯示。地點資料來自 DB-IP。 src/app/pages/stats/stats-page.component.html - 104,106 + 104,105 @@ -18239,7 +18267,7 @@ https://full-selfbrowsing.com/api/telemetry/forget網路或剖析錯誤。 src/app/pages/stats/stats-page.component.ts - 132 + 133 @@ -18247,7 +18275,7 @@ https://full-selfbrowsing.com/api/telemetry/forget星標 src/app/pages/stats/stats-page.component.ts - 139 + 140 @@ -18255,7 +18283,7 @@ https://full-selfbrowsing.com/api/telemetry/forget提交 src/app/pages/stats/stats-page.component.ts - 140 + 141 @@ -18263,7 +18291,7 @@ https://full-selfbrowsing.com/api/telemetry/forget目前活躍 src/app/pages/stats/stats-page.component.ts - 141 + 142 @@ -18271,7 +18299,7 @@ https://full-selfbrowsing.com/api/telemetry/forget權杖 src/app/pages/stats/stats-page.component.ts - 142 + 143 @@ -18279,27 +18307,7 @@ https://full-selfbrowsing.com/api/telemetry/forget熱門 src/app/pages/stats/stats-page.component.ts - 143 - - - - Could not load chart library. - 無法載入圖表程式庫。 - - src/app/pages/stats/stats-page.component.ts - 183 - - - - Could not render the selected chart. - 無法呈現所選圖表。 - - src/app/pages/stats/stats-page.component.ts - 190 - - - src/app/pages/stats/stats-page.component.ts - 862 + 144 @@ -18307,7 +18315,7 @@ https://full-selfbrowsing.com/api/telemetry/forget統計資料正在預熱,稍後將重試。 src/app/pages/stats/stats-page.component.ts - 232 + 233 @@ -18315,7 +18323,7 @@ https://full-selfbrowsing.com/api/telemetry/forget統計回應缺少新鮮度中繼資料。 src/app/pages/stats/stats-page.component.ts - 235 + 236 @@ -18323,7 +18331,7 @@ https://full-selfbrowsing.com/api/telemetry/forget最後一份可用快照已超過 24 小時。 src/app/pages/stats/stats-page.component.ts - 238 + 239 @@ -18331,7 +18339,7 @@ https://full-selfbrowsing.com/api/telemetry/forget統計回應格式有誤。 src/app/pages/stats/stats-page.component.ts - 241 + 242 @@ -18339,7 +18347,7 @@ https://full-selfbrowsing.com/api/telemetry/forget統計資料僅在瀏覽器中可用。 src/app/pages/stats/stats-page.component.ts - 244 + 245 @@ -18347,7 +18355,7 @@ https://full-selfbrowsing.com/api/telemetry/forget程式庫累計星標隨時間變化 src/app/pages/stats/stats-page.component.ts - 252 + 253 @@ -18355,7 +18363,7 @@ https://full-selfbrowsing.com/api/telemetry/forget程式庫累計提交隨時間變化 src/app/pages/stats/stats-page.component.ts - 254 + 255 @@ -18363,7 +18371,7 @@ https://full-selfbrowsing.com/api/telemetry/forget過去 30 天的 FSB 權杖用量 src/app/pages/stats/stats-page.component.ts - 256 + 257 @@ -18371,7 +18379,7 @@ https://full-selfbrowsing.com/api/telemetry/forget已追蹤 MCP 用戶端的占比 src/app/pages/stats/stats-page.component.ts - 258 + 259 @@ -18379,7 +18387,7 @@ https://full-selfbrowsing.com/api/telemetry/forget未知 src/app/pages/stats/stats-page.component.ts - 295 + 296 @@ -18387,15 +18395,15 @@ https://full-selfbrowsing.com/api/telemetry/forget其他 src/app/pages/stats/stats-page.component.ts - 296 + 297 - Globe showing where FSB installs were last seen over the past 365 days, by coarse region - 顯示過去 365 天內 FSB 安裝最後出現位置(依粗略地區)的地球儀 + Globe showing where FSB installs were last seen over the past 365 days, by city, state, or country + 顯示過去 365 天內 FSB 安裝最後出現位置(依城市、州或國家)的地球儀 src/app/pages/stats/stats-page.component.ts - 308 + 309 @@ -18403,7 +18411,7 @@ https://full-selfbrowsing.com/api/telemetry/forget星標總數 src/app/pages/stats/stats-page.component.ts - 317 + 318 @@ -18411,7 +18419,7 @@ https://full-selfbrowsing.com/api/telemetry/forget最近 7 天 src/app/pages/stats/stats-page.component.ts - 318 + 319 @@ -18419,7 +18427,7 @@ https://full-selfbrowsing.com/api/telemetry/forget提交總數 src/app/pages/stats/stats-page.component.ts - 325 + 326 @@ -18427,7 +18435,7 @@ https://full-selfbrowsing.com/api/telemetry/forget最近 30 天 src/app/pages/stats/stats-page.component.ts - 329 + 330 @@ -18435,7 +18443,7 @@ https://full-selfbrowsing.com/api/telemetry/forget活躍代理 src/app/pages/stats/stats-page.component.ts - 343 + 344 @@ -18443,7 +18451,7 @@ https://full-selfbrowsing.com/api/telemetry/forget平均值/回報使用者 src/app/pages/stats/stats-page.component.ts - 347 + 348 @@ -18451,7 +18459,7 @@ https://full-selfbrowsing.com/api/telemetry/forget權杖 src/app/pages/stats/stats-page.component.ts - 357 + 358 @@ -18459,7 +18467,7 @@ https://full-selfbrowsing.com/api/telemetry/forget權杖(24 小時) src/app/pages/stats/stats-page.component.ts - 358 + 359 @@ -18467,7 +18475,7 @@ https://full-selfbrowsing.com/api/telemetry/forget追蹤中的用戶端 src/app/pages/stats/stats-page.component.ts - 364 + 365 @@ -18475,7 +18483,7 @@ https://full-selfbrowsing.com/api/telemetry/forget首位: src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18483,7 +18491,7 @@ https://full-selfbrowsing.com/api/telemetry/forget最高用戶端 src/app/pages/stats/stats-page.component.ts - 365 + 366 @@ -18491,7 +18499,7 @@ https://full-selfbrowsing.com/api/telemetry/forget選擇統計檢視。目前檢視: src/app/pages/stats/stats-page.component.ts - 402 + 403 @@ -18499,7 +18507,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB · 統計 src/app/pages/stats/stats-page.component.ts - 478 + 484 @@ -18507,7 +18515,7 @@ https://full-selfbrowsing.com/api/telemetry/forgetFSB 的即時彙總採用情況、使用量與儲存庫指標。 src/app/pages/stats/stats-page.component.ts - 486 + 492 @@ -18515,7 +18523,7 @@ https://full-selfbrowsing.com/api/telemetry/forget累計星標 src/app/pages/stats/stats-page.component.ts - 974 + 979 @@ -18523,7 +18531,7 @@ https://full-selfbrowsing.com/api/telemetry/forget累計提交 src/app/pages/stats/stats-page.component.ts - 995 + 1000 @@ -18531,7 +18539,7 @@ https://full-selfbrowsing.com/api/telemetry/forget權杖(過去 30 天) src/app/pages/stats/stats-page.component.ts - 1023 + 1028 @@ -18539,7 +18547,23 @@ https://full-selfbrowsing.com/api/telemetry/forget熱門 MCP 客戶端 src/app/pages/stats/stats-page.component.ts - 1046 + 1051 + + + + Could not load chart library. + 無法載入圖表程式庫。 + + src/app/pages/stats/stats-page.component.ts + 1105 + + + + Could not render the selected chart. + 無法呈現所選圖表。 + + src/app/pages/stats/stats-page.component.ts + 1106 diff --git a/showcase/server/data/README.md b/showcase/server/data/README.md index 3e449941..1d7e9301 100644 --- a/showcase/server/data/README.md +++ b/showcase/server/data/README.md @@ -1,15 +1,17 @@ # Telemetry geo dataset (`showcase/server/data/`) -This directory holds the self-hosted IP-to-region dataset used by the anonymous -**aggregate** region telemetry pipeline (quick task `260630-hct`). +This directory holds the self-hosted IP-to-place dataset used by the anonymous +**aggregate** region telemetry pipeline (quick task `260630-hct`). Places are +resolved to city, state/province, and country; publication applies a k>=5 +floor level by level (see `src/telemetry/housekeeper.js` `applyRegionKFloor`). ## Attribution > **IP Geolocation by DB-IP** (), licensed **CC-BY-4.0**. The DB-IP IP-to-City Lite dataset requires attribution (link back to db-ip.com). -This notice, the `scripts/refresh-dbip-dataset.mjs` header, and — if/when a -`/stats` region UI lands — the `/stats` footer satisfy that requirement. Only +This notice, the `scripts/refresh-dbip-dataset.mjs` header, the `/stats` globe +caption ("Place data by DB-IP"), and the privacy policy satisfy that requirement. Only the *dataset* is third-party; the lookup logic in `src/utils/ip-geo.js` (sorted-range binary search) is ours. **No MaxMind, no live third-party geo API.** @@ -18,44 +20,62 @@ live third-party geo API.** | File | Committed? | Purpose | | --- | --- | --- | -| `dbip-city-lite.csv` | **NO** (gitignored) | The real **worldwide** production IPv4 artifact (~70 MB; ~2.1 M merged ranges). Generated by the refresh script; never committed. In production it lives on the Fly `/data` volume, pointed at by `DBIP_DATASET_PATH`. | -| `dbip-city-lite.ipv6.csv` | **NO** (gitignored) | Sibling IPv6 artifact (`start64,end64,country,subdivision`, /64 prefixes). Generate **off the 256 MB Fly VM** (the source CSV is ~685 MB). Pointed at by `DBIP_IPV6_DATASET_PATH`. | +| `dbip-city-lite.csv` | **NO** (gitignored) | The real **worldwide** production IPv4 artifact (`start_ip_int,end_ip_int,country,subdivision,city`; ~160 MB, ~3.4 M ranges). Generated by the refresh script; never committed. In production it lives on the Fly `/data` volume, pointed at by `DBIP_DATASET_PATH`. | +| `dbip-city-lite.ipv6.csv` | **NO** (gitignored) | Sibling IPv6 artifact (`start64,end64,country,subdivision,city`, /64 prefixes; ~228 MB, ~3.8 M ranges). Pointed at by `DBIP_IPV6_DATASET_PATH`. | +| `dbip-city-lite.places.csv` | **NO** (gitignored) | Place centroids (`label,lat,lon`; ~7 MB, ~198 k labels) keyed by the same region labels the ingest route stores. Pointed at by `DBIP_PLACES_DATASET_PATH`. | | `dbip-city-lite.fixture.csv` | **YES** | Tiny deterministic IPv4 fixture for offline geo + aggregation tests. | | `dbip-city-lite.ipv6.fixture.csv` | **YES** | Tiny IPv6 fixture (`2001:db8::/32`, `2405:201::/32`) so native IPv6 (Jio-like) lookups are tested offline. | +| `dbip-city-lite.places.fixture.csv` | **YES** | Tiny place-centroid fixture covering every label the two fixtures above can produce. | | `README.md` | YES | This file. | -`.gitignore` ignores `showcase/server/data/dbip-city-lite.*` **except** -`*.fixture.csv`, so the real artifact can never be committed but the fixture -ships. +`.gitignore` ignores `showcase/server/data/dbip-city-lite.*` **except** the +three `*.fixture.csv` files, so the real artifacts can never be committed but +the fixtures ship. ## Dataset format -`ip-geo.js` reads two compact, sorted range tables (`#`/blank lines ignored): +`ip-geo.js` searches three sorted tables (`#`/blank lines ignored): IPv4 (`dbip-city-lite.csv`): ``` -start_ip_int,end_ip_int,country,subdivision +start_ip_int,end_ip_int,country,subdivision,city ``` IPv6 (`dbip-city-lite.ipv6.csv`): ``` -start64,end64,country,subdivision +start64,end64,country,subdivision,city +``` + +Places (`dbip-city-lite.places.csv`): + +``` +label,lat,lon ``` `start_ip_int`/`end_ip_int` are inclusive **uint32** IPv4 bounds. IPv6 bounds are the inclusive **/64 prefix** (top 64 bits of the address) written as exactly 16 -hex digits, e.g. `2405020100000000,24050201ffffffff,IN,Maharashtra`. DB-IP has no -location detail below a /64, and keying on the prefix halves the table's memory. -Each file is sorted ascending by start. IPv4-mapped IPv6 (`::ffff:a.b.c.d`) -unwraps to IPv4 before lookup; native IPv6 uses the sibling table. Unparseable -input and CIDR `/56` rate-limit keys resolve to `'unknown'`, and so does DB-IP's -`ZZ` pseudo-country for private/reserved space (RFC 1918, loopback, link-local, -ULA including Fly 6PN) -- the refresh script drops those rows and `ip-geo.js` -also maps `ZZ` to `'unknown'` for older files. An IPv6 file in the retired -decimal-halves format does not parse, so IPv6 degrades to `'unknown'` rather than -mis-mapping. +hex digits, e.g. `2405020100000000,24050201ffffffff,IN,Maharashtra,Mumbai`. DB-IP +has no location detail below a /64, and keying on the prefix keeps the table +smaller. Range files are sorted ascending by start; the places file is sorted by +label in JS string order. Four-column range rows (the older state-level format) +still parse, with an empty city. + +A place label is `country[-subdivision][/city]` as built by +`src/utils/region-label.js` — `US-CA/San Jose`, `US-CA`, `US`, `SG/Singapore`. +The places file has one row per label at every level, each the mean position of +that place's upstream ranges (averaged on the sphere, rounded to 0.1°). Public +stats attach it to each published label so the globe can plot cities; nothing +location-shaped is stored per install beyond the label itself. + +IPv4-mapped IPv6 (`::ffff:a.b.c.d`) unwraps to IPv4 before lookup; native IPv6 +uses the sibling table. Unparseable input and CIDR `/56` rate-limit keys resolve +to `'unknown'`, and so does DB-IP's `ZZ` pseudo-country for private/reserved +space (RFC 1918, loopback, link-local, ULA including Fly 6PN) -- the refresh +script drops those rows and `ip-geo.js` also maps `ZZ` to `'unknown'` for older +files. An IPv6 file in the retired decimal-halves format does not parse, so IPv6 +degrades to `'unknown'` rather than mis-mapping. ## Dataset path / env var @@ -64,15 +84,17 @@ mis-mapping. ``` process.env.DBIP_DATASET_PATH || /dbip-city-lite.csv process.env.DBIP_IPV6_DATASET_PATH || sibling of the IPv4 path (*.ipv6.csv / *.ipv6.fixture.csv) +process.env.DBIP_PLACES_DATASET_PATH || sibling of the IPv4 path (*.places.csv / *.places.fixture.csv) ``` -Set `DBIP_DATASET_PATH` to point at the fixture in tests (the IPv6 sibling is -resolved automatically), or set both paths in deployment. +Set `DBIP_DATASET_PATH` to point at the fixture in tests (the IPv6 and places +siblings are resolved automatically), or set all three paths in deployment. ## Graceful degradation (hard requirement) -If the dataset file is **absent, unreadable, or empty**, `deriveRegion(ip)` -returns the literal string `'unknown'` and never throws. **The server boots and +If a dataset file is **absent, unreadable, or empty**, `deriveRegion(ip)` +returns the literal string `'unknown'` (and `placeCentroid(label)` returns +`null`) and never throws. **The server boots and ingests normally with no dataset present.** Region then aggregates as `'unknown'` (and folds into `'Other'` below the k>=5 anonymity floor). This is why the real artifact does not need to ship in the repo. @@ -86,51 +108,54 @@ curl -L -o /tmp/dbip-city-lite.csv.gz \ https://download.db-ip.com/free/dbip-city-lite-YYYY-MM.csv.gz # ~85 MB gz gunzip -k /tmp/dbip-city-lite.csv.gz # ~685 MB -# 2. Transform -> compact, sorted, ADJACENT-SAME-REGION-MERGED IPv4 + IPv6 tables. -# Do this on a machine with RAM to spare (not the 256 MB Fly VM): -node showcase/server/scripts/refresh-dbip-dataset.mjs --in /tmp/dbip-city-lite.csv -# (optional: --out --ipv6-out ; defaults to DBIP_DATASET_PATH / -# a sibling *.ipv6.csv, or /dbip-city-lite.csv and .ipv6.csv) +# 2. Transform -> compact, sorted, ADJACENT-SAME-PLACE-MERGED IPv4 + IPv6 tables +# plus the places file. Do this on a machine with RAM to spare (the transform +# peaks around 4.5 GB; not the 256 MB Fly VM): +node --max-old-space-size=8192 showcase/server/scripts/refresh-dbip-dataset.mjs --in /tmp/dbip-city-lite.csv +# (optional: --out --ipv6-out --places-out ; defaults to +# DBIP_*_DATASET_PATH / siblings of the IPv4 output, or this dir) ``` The transform is worldwide (all countries). It merges consecutive -same-`(country, subdivision)` adjacent ranges so the output stays small. The -2026-09 release (7.7 M source rows) produced ~2.07 M IPv4 ranges / ~72 MB and -~2.19 M IPv6 /64 ranges / ~105 MB. Running the script with no `--in` prints the -download URL + the format spec (and the DB-IP attribution) and exits non-zero. - -The loader reads each file in 1 MiB chunks into compact typed arrays (no -whole-file Buffer/string). Measured with that release on the production server -image in a 256 MB, no-swap, 1-CPU container: the IPv4 table adds ~24 MB and -loads in ~0.5 s; the IPv6 table adds ~42 MB and loads in ~0.5–0.8 s. The server -settles around 150 MB RSS with both loaded (~104 MB with IPv4 only), peaking -near 165 MB under a 3,000-request burst. +same-`(country, subdivision, city)` adjacent ranges. The 2026-09 release (7.7 M +source rows) produced ~3.39 M IPv4 ranges / ~160 MB, ~3.82 M IPv6 /64 ranges / +~228 MB, and ~198 k place centroids / ~7 MB, in about 40 s. Running the script +with no `--in` prints the download URL + the format spec (and the DB-IP +attribution) and exits non-zero. + +Nothing is loaded into memory: `ip-geo.js` binary-searches each file in place +with positioned 4 KiB reads (about 16 per lookup, ~35 µs once the page cache is +warm) and holds one 64 KiB buffer per file. Loading the city-level tables as +typed arrays instead would take ~110 MB, which does not fit beside the server on +the 256 MB VM. Checked against the previous in-memory state-level lookup on the +same release, 60,000 sampled IPv4/IPv6 addresses resolved to the same country +and subdivision, every hit carried a city, and every resulting label had a +centroid. ## Production deployment (Fly `/data` volume) -`DBIP_DATASET_PATH = '/data/dbip-city-lite.csv'` and -`DBIP_IPV6_DATASET_PATH = '/data/dbip-city-lite.ipv6.csv'` are set in `fly.toml`. -The datasets are **not** baked into the Docker image (they would bloat every -deploy); they live on the existing `fsb_data` volume mounted at `/data`. To -deploy/refresh: +`fly.toml` points `DBIP_DATASET_PATH`, `DBIP_IPV6_DATASET_PATH`, and +`DBIP_PLACES_DATASET_PATH` at `/data/dbip-city/`. The datasets are **not** baked +into the Docker image (they would bloat every deploy); they live on the existing +`fsb_data` volume mounted at `/data`. To refresh: ```bash -# After generating both files (above), deploy the app (env vars already set): -fly deploy - -# Upload the generated datasets to the volume: -fly ssh sftp shell -a fsb-server -# then: -# put showcase/server/data/dbip-city-lite.csv /data/dbip-city-lite.csv -# put showcase/server/data/dbip-city-lite.ipv6.csv /data/dbip-city-lite.ipv6.csv - -# The lazy loader caches a "no dataset" result on first miss, so restart the -# machine once after the upload so it re-reads the now-present files: +# Upload compressed copies (sftp is slow; the CSVs gzip ~6x), then unpack in place: +fly ssh console -a fsb-server -C "mkdir -p /data/dbip-city.new" +fly ssh sftp put -a fsb-server dbip-city-lite.csv.gz /data/dbip-city.new/dbip-city-lite.csv.gz +fly ssh sftp put -a fsb-server dbip-city-lite.ipv6.csv.gz /data/dbip-city.new/dbip-city-lite.ipv6.csv.gz +fly ssh sftp put -a fsb-server dbip-city-lite.places.csv.gz /data/dbip-city.new/dbip-city-lite.places.csv.gz +fly ssh console -a fsb-server -C "sh -c 'cd /data/dbip-city.new && gunzip -f *.gz && sha256sum *.csv'" +# Compare the sums with the local files, swap the directory, and restart so the +# server reopens the files (an open descriptor keeps reading the old inode): +fly ssh console -a fsb-server -C "sh -c 'rm -rf /data/dbip-city.old && mv /data/dbip-city /data/dbip-city.old && mv /data/dbip-city.new /data/dbip-city'" fly machine restart -a fsb-server ``` -Because of graceful degradation, setting the env var / deploying **before** the -file exists is safe — regions resolve to `'unknown'` until the upload + restart. +The volume is ~1 GB and one generation of the files is ~395 MB, so delete +`/data/dbip-city.old` once the new files are confirmed. Because of graceful +degradation, deploying **before** the files exist is safe — regions resolve to +`'unknown'` until the upload + restart. ## Privacy posture @@ -138,8 +163,9 @@ Plaintext IP is **never** stored or logged. The ingest route (`src/routes/telemetry.js`) references the client IP (`clientIp(req)`) exactly three times — for the rate-limit HMAC hash, `deriveRegion(...)`, and `classifyIp(...)` — all **inline**, all discarding the plaintext immediately. -The coarse region label and address family (`geo_kind`) are kept on the raw -event (7-day retention) and on the install's daily rollup (365-day retention, -deleted by the erase endpoint) so the stats globe survives the event wipe. -Only **k>=5-floored aggregates** are ever published. See +The coarse place label (city at most) and address family (`geo_kind`) are kept +on the raw event (7-day retention) and on the install's daily rollup (365-day +retention, deleted by the erase endpoint) so the stats globe survives the event +wipe. Only **k>=5-floored aggregates** are ever published, each at the most +specific level (city, then state, then country) that clears the floor. See `tests/server-no-ip-leak.test.js`. diff --git a/showcase/server/data/dbip-city-lite.fixture.csv b/showcase/server/data/dbip-city-lite.fixture.csv index f943a1a7..21b54b9a 100644 --- a/showcase/server/data/dbip-city-lite.fixture.csv +++ b/showcase/server/data/dbip-city-lite.fixture.csv @@ -1,9 +1,9 @@ # dbip-city-lite.fixture.csv -- TINY committed test fixture (quick task 260630-hct). # -# Format mirrors the compact range table that ip-geo.js parses and that +# Format mirrors the compact range table that ip-geo.js searches and that # scripts/refresh-dbip-dataset.mjs emits from the upstream DB-IP CSV: # -# start_ip_int,end_ip_int,country,subdivision +# start_ip_int,end_ip_int,country,subdivision,city # # where start/end are inclusive uint32 forms of the IPv4 range bounds. Rows must # be sorted ascending by start_ip_int (the module binary-searches on that key). @@ -11,19 +11,21 @@ # # This is NOT real geolocation data -- it is a handful of deterministic ranges # (well-known/reserved IPv4 blocks) chosen only so the geo + aggregation tests -# run offline. The US + worldwide (GB/DE/IN/BR) rows below are synthetic; BR/São -# Paulo carries a multibyte (accented) subdivision on purpose to prove non-ASCII -# round-trips through the typed-array loader. The real ~tens-of-MB worldwide -# DB-IP IP-to-City Lite artifact is generated by the refresh script and is NOT -# committed (see data/README.md + .gitignore). +# run offline. The US + worldwide (GB/DE/IN/BR/SG) rows below are synthetic; +# BR/São Paulo carries a multibyte (accented) subdivision and city on purpose to +# prove non-ASCII round-trips, the US/Texas row keeps the older four-column +# shape (no city) to prove state-level files still parse, and SG has a city but +# no subdivision. The real worldwide DB-IP IP-to-City Lite artifact is generated +# by the refresh script and is NOT committed (see data/README.md + .gitignore). # # Attribution for the real dataset: IP Geolocation by DB-IP (https://db-ip.com), CC-BY-4.0. 16843008,16843263,US,Texas -33686016,33686271,US,California -84215040,84215295,GB,England -134744064,134744319,US,California -151587072,151587327,US,New York -168430080,168430335,DE,Bavaria -336860160,336860415,IN,Maharashtra -505290240,505290495,BR,São Paulo -3405803776,3405804031,AU,Victoria +33686016,33686271,US,California,Los Angeles +84215040,84215295,GB,England,London +134744064,134744319,US,California,Mountain View +151587072,151587327,US,New York,New York +168430080,168430335,DE,Bavaria,Munich +336860160,336860415,IN,Maharashtra,Mumbai +505290240,505290495,BR,São Paulo,São Paulo +673720320,673720575,SG,,Singapore +3405803776,3405804031,AU,Victoria,Melbourne diff --git a/showcase/server/data/dbip-city-lite.ipv6.fixture.csv b/showcase/server/data/dbip-city-lite.ipv6.fixture.csv index e7775bec..db6fd4d7 100644 --- a/showcase/server/data/dbip-city-lite.ipv6.fixture.csv +++ b/showcase/server/data/dbip-city-lite.ipv6.fixture.csv @@ -1,12 +1,12 @@ # dbip-city-lite.ipv6.fixture.csv -- TINY committed IPv6 test fixture. # -# Format: start64,end64,country,subdivision +# Format: start64,end64,country,subdivision,city # start64/end64 are the inclusive /64 prefixes (top 64 bits of the IPv6 # address) as exactly 16 hex digits. Sorted ascending by start64. # -# 2001:db8::/32 -> AU/Victoria (docs prefix; not a real geo) -# 2405:201::/32 -> IN/Maharashtra (synthetic stand-in for Indian IPv6 / Jio) +# 2001:db8::/32 -> AU/Victoria/Melbourne (docs prefix; not a real geo) +# 2405:201::/32 -> IN/Maharashtra/Mumbai (synthetic stand-in for Indian IPv6 / Jio) # # Attribution for the real dataset: IP Geolocation by DB-IP (https://db-ip.com), CC-BY-4.0. -20010db800000000,20010db8ffffffff,AU,Victoria -2405020100000000,24050201ffffffff,IN,Maharashtra +20010db800000000,20010db8ffffffff,AU,Victoria,Melbourne +2405020100000000,24050201ffffffff,IN,Maharashtra,Mumbai diff --git a/showcase/server/data/dbip-city-lite.places.fixture.csv b/showcase/server/data/dbip-city-lite.places.fixture.csv new file mode 100644 index 00000000..3ce39223 --- /dev/null +++ b/showcase/server/data/dbip-city-lite.places.fixture.csv @@ -0,0 +1,33 @@ +# dbip-city-lite.places.fixture.csv -- TINY committed place-centroid fixture. +# +# Format: label,lat,lon -- one approximate centroid per region label at every +# level (country, subdivision, city; see src/utils/region-label.js), sorted by +# label in JS string order. Labels cover the ranges in the sibling IPv4/IPv6 +# fixtures; coordinates are rounded real-world centroids for readability, not +# output of the refresh script. +# +# Attribution for the real dataset: IP Geolocation by DB-IP (https://db-ip.com), CC-BY-4.0. +AU,-25.3,134 +AU-Victoria,-37,144.3 +AU-Victoria/Melbourne,-37.8,145 +BR,-14.2,-51.9 +BR-São-Paulo,-22.3,-48.6 +BR-São-Paulo/São Paulo,-23.6,-46.6 +DE,51.2,10.4 +DE-Bavaria,48.8,11.5 +DE-Bavaria/Munich,48.1,11.6 +GB,53.4,-1.9 +GB-England,52.4,-1.5 +GB-England/London,51.5,-0.1 +IN,21.1,78.4 +IN-Maharashtra,19.4,75.3 +IN-Maharashtra/Mumbai,19.1,72.9 +SG,1.4,103.8 +SG/Singapore,1.3,103.9 +US,38.6,-93.2 +US-CA,36.7,-119.6 +US-CA/Los Angeles,34.1,-118.2 +US-CA/Mountain View,37.4,-122.1 +US-NY,42.2,-74.9 +US-NY/New York,40.7,-74 +US-TX,31.2,-97.8 diff --git a/showcase/server/scripts/refresh-dbip-dataset.mjs b/showcase/server/scripts/refresh-dbip-dataset.mjs index 9ca17a61..0656b8d3 100644 --- a/showcase/server/scripts/refresh-dbip-dataset.mjs +++ b/showcase/server/scripts/refresh-dbip-dataset.mjs @@ -6,15 +6,22 @@ * IP Geolocation by DB-IP (https://db-ip.com), CC-BY-4.0. * ============================================================================ * This script transforms the upstream DB-IP IP-to-City Lite CSV into the - * compact range table that showcase/server/src/utils/ip-geo.js reads: + * compact sorted tables that showcase/server/src/utils/ip-geo.js searches: * - * start_ip_int,end_ip_int,country,subdivision (IPv4) - * start64,end64,country,subdivision (IPv6 /64 prefixes, 16 hex digits) + * start_ip_int,end_ip_int,country,subdivision,city (IPv4) + * start64,end64,country,subdivision,city (IPv6 /64 prefixes, 16 hex digits) + * label,lat,lon (places, sorted by label) * - * (inclusive bounds, each file sorted ascending). The IPv4 output is written to - * the production dataset path consumed by ip-geo.js + * Range bounds are inclusive and each file is sorted ascending. The IPv4 output + * is written to the production dataset path consumed by ip-geo.js * (process.env.DBIP_DATASET_PATH || showcase/server/data/dbip-city-lite.csv). - * IPv6 goes to --ipv6-out or a sibling `*.ipv6.csv` (DBIP_IPV6_DATASET_PATH). + * IPv6 goes to --ipv6-out or a sibling `*.ipv6.csv` (DBIP_IPV6_DATASET_PATH), + * places to --places-out or a sibling `*.places.csv` (DBIP_PLACES_DATASET_PATH). + * + * The places file holds one approximate centroid per region label at every + * level (country 'US', subdivision 'US-CA', city 'US-CA/San Jose'), labelled by + * the same regionLabel() the ingest route stores. Each centroid is the mean + * position of that place's upstream ranges, rounded to 0.1 degree. * * The real artifacts are tens of MB and are NOT committed (see data/README.md + * .gitignore -- the data/dbip-city-lite.* glob is ignored EXCEPT *.fixture.csv). @@ -30,7 +37,7 @@ * * 2. Run: * node showcase/server/scripts/refresh-dbip-dataset.mjs --in - * optionally with --out and --ipv6-out . + * optionally with --out , --ipv6-out and --places-out . * * Run: node showcase/server/scripts/refresh-dbip-dataset.mjs --in dbip-city-lite.csv */ @@ -41,24 +48,29 @@ import { createReadStream, mkdirSync, createWriteStream, existsSync } from 'node import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { createInterface } from 'node:readline'; +import { createRequire } from 'node:module'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +const require = createRequire(import.meta.url); +const { regionLabel } = require('../src/utils/region-label.js'); // Default production artifact path (mirrors ip-geo.js DEFAULT_DATASET_PATH). const DEFAULT_OUT = join(__dirname, '..', 'data', 'dbip-city-lite.csv'); const DEFAULT_IPV6_OUT = join(__dirname, '..', 'data', 'dbip-city-lite.ipv6.csv'); +const DEFAULT_PLACES_OUT = join(__dirname, '..', 'data', 'dbip-city-lite.places.csv'); const DOWNLOAD_URL = 'https://db-ip.com/db/download/ip-to-city-lite'; const ATTRIBUTION = 'IP Geolocation by DB-IP (https://db-ip.com), CC-BY-4.0'; -function siblingIpv6Path(ipv4Path) { - if (typeof ipv4Path !== 'string' || ipv4Path === '') return DEFAULT_IPV6_OUT; +/** Sibling artifact for an IPv4 output path (mirrors ip-geo.js siblingPath). */ +function siblingPath(ipv4Path, kind, fallback) { + if (typeof ipv4Path !== 'string' || ipv4Path === '') return fallback; if (ipv4Path.endsWith('.fixture.csv')) { - return ipv4Path.replace(/\.fixture\.csv$/, '.ipv6.fixture.csv'); + return ipv4Path.replace(/\.fixture\.csv$/, `.${kind}.fixture.csv`); } - if (ipv4Path.endsWith('.csv')) return ipv4Path.slice(0, -4) + '.ipv6.csv'; - return ipv4Path + '.ipv6'; + if (ipv4Path.endsWith('.csv')) return ipv4Path.slice(0, -4) + `.${kind}.csv`; + return `${ipv4Path}.${kind}`; } function parseArgs(argv) { @@ -66,11 +78,13 @@ function parseArgs(argv) { in: null, out: process.env.DBIP_DATASET_PATH || DEFAULT_OUT, ipv6Out: process.env.DBIP_IPV6_DATASET_PATH || null, + placesOut: process.env.DBIP_PLACES_DATASET_PATH || null, }; for (let i = 0; i < argv.length; i++) { if (argv[i] === '--in') args.in = argv[++i]; else if (argv[i] === '--out') args.out = argv[++i]; else if (argv[i] === '--ipv6-out') args.ipv6Out = argv[++i]; + else if (argv[i] === '--places-out') args.placesOut = argv[++i]; } return args; } @@ -164,6 +178,39 @@ function splitCsvLine(line) { return out; } +function sameLabel(a, b) { + return a[2] === b[2] && a[3] === b[3] && a[4] === b[4]; +} + +/** Add one upstream row's position to the country, subdivision and city labels. */ +function addPlace(placeSums, country, subdivision, city, lat, lon) { + if (!Number.isFinite(lat) || !Number.isFinite(lon)) return; + if (lat < -90 || lat > 90 || lon < -180 || lon > 180) return; + const phi = (lat * Math.PI) / 180; + const lambda = (lon * Math.PI) / 180; + const x = Math.cos(phi) * Math.cos(lambda); + const y = Math.cos(phi) * Math.sin(lambda); + const z = Math.sin(phi); + const labels = new Set([ + regionLabel({ country }), + regionLabel({ country, subdivision }), + regionLabel({ country, subdivision, city }), + ]); + for (const label of labels) { + if (label === 'unknown') continue; + const sum = placeSums.get(label); + if (sum) { sum[0] += x; sum[1] += y; sum[2] += z; } else placeSums.set(label, [x, y, z]); + } +} + +function centroidOf([x, y, z]) { + const round = (deg) => Math.round(deg * 10) / 10; + const lat = (Math.atan2(z, Math.hypot(x, y)) * 180) / Math.PI; + const lon = (Math.atan2(y, x) * 180) / Math.PI; + // -0 would print as 0 anyway; normalise so the file never carries '-0'. + return [round(lat) || 0, round(lon) || 0]; +} + function printSpecAndExit() { console.error('refresh-dbip-dataset: no --in provided.'); console.error(''); @@ -172,14 +219,17 @@ function printSpecAndExit() { console.error(' 1. Download the free "IP to City Lite" CSV from DB-IP:'); console.error(` ${DOWNLOAD_URL}`); console.error(' (gunzip the .csv.gz first).'); - console.error(' 2. Re-run with: --in [--out ] [--ipv6-out ]'); + console.error(' 2. Re-run with: --in [--out ] [--ipv6-out ] [--places-out ]'); console.error(''); console.error(' IPv4 output (consumed by src/utils/ip-geo.js):'); - console.error(' start_ip_int,end_ip_int,country,subdivision'); + console.error(' start_ip_int,end_ip_int,country,subdivision,city'); console.error(' (inclusive uint32 IPv4 bounds, sorted ascending)'); console.error(' IPv6 output (sibling *.ipv6.csv unless --ipv6-out / DBIP_IPV6_DATASET_PATH):'); - console.error(' start64,end64,country,subdivision'); + console.error(' start64,end64,country,subdivision,city'); console.error(' (inclusive /64 prefixes as 16 hex digits, sorted ascending)'); + console.error(' Places output (sibling *.places.csv unless --places-out / DBIP_PLACES_DATASET_PATH):'); + console.error(' label,lat,lon'); + console.error(' (one approximate centroid per region label, sorted by label)'); process.exit(2); } @@ -194,15 +244,18 @@ async function main() { } const outPath = resolve(args.out); - const ipv6OutPath = resolve(args.ipv6Out || siblingIpv6Path(outPath)); - mkdirSync(dirname(outPath), { recursive: true }); - mkdirSync(dirname(ipv6OutPath), { recursive: true }); + const ipv6OutPath = resolve(args.ipv6Out || siblingPath(outPath, 'ipv6', DEFAULT_IPV6_OUT)); + const placesOutPath = resolve(args.placesOut || siblingPath(outPath, 'places', DEFAULT_PLACES_OUT)); + for (const p of [outPath, ipv6OutPath, placesOutPath]) mkdirSync(dirname(p), { recursive: true }); // Stream-transform: read upstream rows, emit IPv4 and IPv6 range rows. We // collect into memory to sort before writing (ip-geo.js binary-searches a // sorted table). Run this OFF the 256 MB Fly VM -- the source CSV is ~685 MB. const rows = []; const v6rows = []; + // label -> [sum x, sum y, sum z] of unit vectors, so a place straddling the + // antimeridian (Fiji, Chukotka) averages to the right side of the globe. + const placeSums = new Map(); const rl = createInterface({ input: createReadStream(inPath, 'utf8'), crlfDelay: Infinity }); let lineNo = 0; @@ -210,47 +263,49 @@ async function main() { lineNo++; if (line.trim() === '') continue; const cols = splitCsvLine(line); - // Expected upstream order: ip_start, ip_end, continent, country, stateprov, ... + // Expected upstream order: ip_start, ip_end, continent, country, stateprov, city, lat, lon if (cols.length < 5) continue; const country = (cols[3] || '').trim(); - const subdivision = (cols[4] || '').trim(); // DB-IP's ZZ marks private/reserved space; it is not a place. if (country === '' || country === 'ZZ') continue; - const countrySafe = country.replace(/,/g, ' '); - const subSafe = subdivision.replace(/,/g, ' '); + // Commas separate the output columns, so none may survive in a name. + const clean = (name) => (name || '').replace(/,/g, ' ').replace(/\s+/g, ' ').trim(); + const countrySafe = clean(country); + const subSafe = clean(cols[4]); + const citySafe = clean(cols[5]); + addPlace(placeSums, countrySafe, subSafe, citySafe, Number(cols[6]), Number(cols[7])); const startInt = ipv4ToInt(cols[0]); const endInt = ipv4ToInt(cols[1]); if (startInt !== null && endInt !== null) { if (endInt < startInt) continue; - rows.push([startInt, endInt, countrySafe, subSafe]); + rows.push([startInt, endInt, countrySafe, subSafe, citySafe]); continue; } const startV6 = ipv6ToPrefix64(cols[0]); const endV6 = ipv6ToPrefix64(cols[1]); if (startV6 === null || endV6 === null || endV6 < startV6) continue; - v6rows.push([startV6, endV6, countrySafe, subSafe]); + v6rows.push([startV6, endV6, countrySafe, subSafe, citySafe]); } rows.sort((a, b) => a[0] - b[0]); // Stable sort: rows sharing a /64 keep upstream (ascending address) order. v6rows.sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)); - // Range-merge: collapse CONSECUTIVE rows that share country+subdivision AND - // whose ranges are contiguous or overlapping (nextStart <= lastEnd + 1) into a - // single range. The IP-to-City Lite source splits ranges by *city*; collapsing - // to (country, subdivision) removes a large fraction of rows, which keeps the - // compact output small enough to (a) stay well under V8's ~512 MiB string cap - // when ip-geo.js reads it and (b) fit the 256 MB Fly VM after parsing. Merge - // only inspects the immediate predecessor, so the prior sort is required. + // Range-merge: collapse CONSECUTIVE rows that share country, subdivision and + // city AND whose ranges are contiguous or overlapping (nextStart <= lastEnd + + // 1) into a single range. Upstream often splits one city's block into several + // rows (different coordinates within the city), so this trims the table a + // little. Merge only inspects the immediate predecessor, so the prior sort is + // required. const merged = []; for (const r of rows) { const last = merged.length > 0 ? merged[merged.length - 1] : null; - if (last && last[2] === r[2] && last[3] === r[3] && r[0] <= last[1] + 1) { + if (last && sameLabel(last, r) && r[0] <= last[1] + 1) { if (r[1] > last[1]) last[1] = r[1]; } else { - merged.push([r[0], r[1], r[2], r[3]]); + merged.push([r[0], r[1], r[2], r[3], r[4]]); } } @@ -260,13 +315,13 @@ async function main() { // same-label prefixes merge exactly like IPv4. const mergedV6 = []; for (const row of v6rows) { - const r = [row[0], row[1], row[2], row[3]]; + const r = [row[0], row[1], row[2], row[3], row[4]]; const last = mergedV6.length > 0 ? mergedV6[mergedV6.length - 1] : null; if (last && r[0] <= last[1]) { if (r[1] <= last[1]) continue; r[0] = last[1] + 1n; } - if (last && last[2] === r[2] && last[3] === r[3] && r[0] <= last[1] + 1n) { + if (last && sameLabel(last, r) && r[0] <= last[1] + 1n) { last[1] = r[1]; continue; } @@ -276,27 +331,40 @@ async function main() { const ws = createWriteStream(outPath, 'utf8'); ws.write(`# Generated by refresh-dbip-dataset.mjs from a DB-IP IP-to-City Lite source CSV.\n`); ws.write(`# ${ATTRIBUTION}\n`); - ws.write(`# Format: start_ip_int,end_ip_int,country,subdivision (uint32 IPv4, sorted ascending; adjacent same-region ranges merged).\n`); + ws.write(`# Format: start_ip_int,end_ip_int,country,subdivision,city (uint32 IPv4, sorted ascending; adjacent same-place ranges merged).\n`); for (const r of merged) { - ws.write(`${r[0]},${r[1]},${r[2]},${r[3]}\n`); + ws.write(`${r[0]},${r[1]},${r[2]},${r[3]},${r[4]}\n`); } await new Promise((res, rej) => { ws.end((err) => (err ? rej(err) : res())); }); const ws6 = createWriteStream(ipv6OutPath, 'utf8'); ws6.write(`# Generated by refresh-dbip-dataset.mjs from a DB-IP IP-to-City Lite source CSV.\n`); ws6.write(`# ${ATTRIBUTION}\n`); - ws6.write(`# Format: start64,end64,country,subdivision (inclusive IPv6 /64 prefixes as 16 hex digits, sorted ascending; adjacent same-region ranges merged).\n`); + ws6.write(`# Format: start64,end64,country,subdivision,city (inclusive IPv6 /64 prefixes as 16 hex digits, sorted ascending; adjacent same-place ranges merged).\n`); for (const r of mergedV6) { - ws6.write(`${hex64(r[0])},${hex64(r[1])},${r[2]},${r[3]}\n`); + ws6.write(`${hex64(r[0])},${hex64(r[1])},${r[2]},${r[3]},${r[4]}\n`); } await new Promise((res, rej) => { ws6.end((err) => (err ? rej(err) : res())); }); + // ip-geo.js compares labels as JS strings, so sort with the same comparison. + const places = [...placeSums.keys()].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)); + const wsp = createWriteStream(placesOutPath, 'utf8'); + wsp.write(`# Generated by refresh-dbip-dataset.mjs from a DB-IP IP-to-City Lite source CSV.\n`); + wsp.write(`# ${ATTRIBUTION}\n`); + wsp.write(`# Format: label,lat,lon (approximate centroid per region label, 0.1 degree; sorted by label).\n`); + for (const label of places) { + const [lat, lon] = centroidOf(placeSums.get(label)); + wsp.write(`${label},${lat},${lon}\n`); + } + await new Promise((res, rej) => { wsp.end((err) => (err ? rej(err) : res())); }); + const reduction = rows.length > 0 ? Math.round((1 - merged.length / rows.length) * 100) : 0; const reduction6 = v6rows.length > 0 ? Math.round((1 - mergedV6.length / v6rows.length) * 100) : 0; console.log(`refresh-dbip-dataset: wrote ${merged.length} IPv4 ranges to ${outPath}`); console.log(` Merged from ${rows.length} raw IPv4 ranges (${reduction}% reduction).`); console.log(`refresh-dbip-dataset: wrote ${mergedV6.length} IPv6 ranges to ${ipv6OutPath}`); console.log(` Merged from ${v6rows.length} raw IPv6 ranges (${reduction6}% reduction).`); + console.log(`refresh-dbip-dataset: wrote ${places.length} place centroids to ${placesOutPath}`); console.log(` Source lines read: ${lineNo}`); console.log(` ${ATTRIBUTION}`); } diff --git a/showcase/server/src/db/schema.js b/showcase/server/src/db/schema.js index 6220c519..6a2b70f5 100644 --- a/showcase/server/src/db/schema.js +++ b/showcase/server/src/db/schema.js @@ -240,8 +240,8 @@ function initializeDatabase(db) { -- telemetry_events: raw event log; 7-day retention enforced by housekeeper. event_id is PRIMARY KEY -- so INSERT OR IGNORE satisfies BEAT-04 (client-side replay-dedup). ip_hash stores ONLY the -- HMAC-SHA256(plaintext_ip, todays_salt); plaintext IP never persisted. - -- Quick task 260630-hct -- short-lived region column (coarse country/US-state - -- label, e.g. US-CA, DEFAULT 'unknown'). Derived at ingest from Fly-Client-IP, + -- Quick task 260630-hct -- short-lived region column (coarse city/state/country + -- label, e.g. US-CA/San Jose, DEFAULT 'unknown'). Derived at ingest from Fly-Client-IP, -- copied onto 365-day rollups (durable anonymous last-known location), and -- the raw event row is still dropped by 7-day retention. geo_kind stores -- only the address family that produced the label (ipv4 / ipv6 / ...), never @@ -270,7 +270,7 @@ function initializeDatabase(db) { -- telemetry_rollups_daily: per-UUID per-day aggregates; 365-day retention; powers Phase 274. -- region / geo_kind are the install's latest coarse location for that UTC day - -- (anonymous: country or US-state label + address family, never an IP). + -- (anonymous: city/state/country label + address family, never an IP). CREATE TABLE IF NOT EXISTS telemetry_rollups_daily ( install_uuid TEXT NOT NULL, day_utc TEXT NOT NULL, diff --git a/showcase/server/src/routes/public-stats.js b/showcase/server/src/routes/public-stats.js index d61f629a..f69f1446 100644 --- a/showcase/server/src/routes/public-stats.js +++ b/showcase/server/src/routes/public-stats.js @@ -34,7 +34,8 @@ const express = require('express'); const crypto = require('crypto'); const activeTracker = require('../telemetry/active-tracker'); -const { applyDistinctKFloor, REGION_K_FLOOR } = require('../telemetry/housekeeper'); +const { applyRegionKFloor, REGION_K_FLOOR } = require('../telemetry/housekeeper'); +const { placeCentroid } = require('../utils/ip-geo'); // 30-second in-process memo TTL. const MEMO_TTL_MS = 30 * 1000; @@ -87,6 +88,26 @@ function isoFromMsOrNull(value) { return Number.isFinite(value) && value > 0 ? new Date(value).toISOString() : null; } +/** + * One public region entry. lat/lon are the dataset's approximate centroid for + * the place named by the label (city, subdivision, or country), so the globe + * can plot places it has no built-in table for; they are omitted for 'Other', + * 'unknown', and labels the places file does not know. + * + * @param {string} label + * @param {unknown} uniq + * @returns {{label:string, uniq:number, lat?:number, lon?:number}} + */ +function publicRegion(label, uniq) { + const entry = { label, uniq: Number.isInteger(uniq) ? uniq : 0 }; + const centroid = placeCentroid(label); + if (centroid) { + entry.lat = centroid.lat; + entry.lon = centroid.lon; + } + return entry; +} + /** * Build the FSBTelemetryHeadline JSON object. * @@ -109,9 +130,9 @@ function buildHeadlineJson(queries, nowMs = Date.now()) { // follows the same shape (housekeeper writes {agent, uniq} -> rename agent). const popularMcpRaw = safeParseArray(rows.latest_global.popular_mcp_json); const popularAgentRaw = safeParseArray(rows.latest_global.popular_agent_json); - // Quick task 260630-hct -- region breakdown. Stored as {region, uniq} (already - // k>=5-floored by the housekeeper; sub-floor regions folded into 'Other'); map - // region -> label for the public contract, mirroring the popular_mcp_clients shape. + // Region breakdown. Stored as {region, uniq}, already k>=5-floored by the + // housekeeper (city -> subdivision -> country -> 'Other'); mapped to the + // public {label, uniq} shape plus the place's centroid when one is known. const popularRegionRaw = safeParseArray(rows.latest_global.popular_region_json); const popular_mcp_clients = popularMcpRaw.map((r) => ({ label: typeof r.label === 'string' ? r.label @@ -125,21 +146,17 @@ function buildHeadlineJson(queries, nowMs = Date.now()) { : 'unknown', uniq: Number.isInteger(r.uniq) ? r.uniq : 0, })); - const popular_regions = popularRegionRaw.map((r) => ({ - label: typeof r.label === 'string' ? r.label - : typeof r.region === 'string' ? r.region - : 'unknown', - uniq: Number.isInteger(r.uniq) ? r.uniq : 0, - })); - const users_by_region_365d = applyDistinctKFloor( + const popular_regions = popularRegionRaw.map((r) => publicRegion( + typeof r.label === 'string' ? r.label + : typeof r.region === 'string' ? r.region + : 'unknown', + r.uniq + )); + const users_by_region_365d = applyRegionKFloor( queries.lastKnownRollupRegions(activeSnapshotMs), - 'region', 'install_uuid', REGION_K_FLOOR - ).map((r) => ({ - label: typeof r.region === 'string' ? r.region : 'unknown', - uniq: Number.isInteger(r.uniq) ? r.uniq : 0, - })); + ).map((r) => publicRegion(r.region, r.uniq)); const avg_agents_per_reporting_user = active_agents_reporting_users_now > 0 ? Math.round((active_agents_now / active_agents_reporting_users_now) * 10) / 10 diff --git a/showcase/server/src/routes/telemetry.js b/showcase/server/src/routes/telemetry.js index 1a3227bd..0b1c21e4 100644 --- a/showcase/server/src/routes/telemetry.js +++ b/showcase/server/src/routes/telemetry.js @@ -33,6 +33,7 @@ const { isValidUuidV4 } = require('../utils/telemetry-hash'); // argument, used once then discarded; only the coarse region label and the // address family are retained, and only k>=5-floored aggregates are published. const { deriveRegion, classifyIp } = require('../utils/ip-geo'); +const { regionLabel } = require('../utils/region-label'); const { clientIp } = require('../utils/client-ip'); const { createTelemetryRateLimiter, @@ -81,51 +82,6 @@ const ACTIVE_COUNT_VERSION = 2; // must not be reinterpreted as the install's current agent population. const ACTIVE_AGENT_LIVENESS_MAX_AGE_MS = 10 * 60 * 1000; -// Quick task 260630-hct -- US state name -> USPS 2-letter code, so the stored -// region label is compact (e.g. "US-CA") rather than a free-form state string. -const US_STATE_CODES = { - 'Alabama': 'AL', 'Alaska': 'AK', 'Arizona': 'AZ', 'Arkansas': 'AR', - 'California': 'CA', 'Colorado': 'CO', 'Connecticut': 'CT', 'Delaware': 'DE', - 'Florida': 'FL', 'Georgia': 'GA', 'Hawaii': 'HI', 'Idaho': 'ID', - 'Illinois': 'IL', 'Indiana': 'IN', 'Iowa': 'IA', 'Kansas': 'KS', - 'Kentucky': 'KY', 'Louisiana': 'LA', 'Maine': 'ME', 'Maryland': 'MD', - 'Massachusetts': 'MA', 'Michigan': 'MI', 'Minnesota': 'MN', 'Mississippi': 'MS', - 'Missouri': 'MO', 'Montana': 'MT', 'Nebraska': 'NE', 'Nevada': 'NV', - 'New Hampshire': 'NH', 'New Jersey': 'NJ', 'New Mexico': 'NM', 'New York': 'NY', - 'North Carolina': 'NC', 'North Dakota': 'ND', 'Ohio': 'OH', 'Oklahoma': 'OK', - 'Oregon': 'OR', 'Pennsylvania': 'PA', 'Rhode Island': 'RI', 'South Carolina': 'SC', - 'South Dakota': 'SD', 'Tennessee': 'TN', 'Texas': 'TX', 'Utah': 'UT', - 'Vermont': 'VT', 'Virginia': 'VA', 'Washington': 'WA', 'West Virginia': 'WV', - 'Wisconsin': 'WI', 'Wyoming': 'WY', 'District of Columbia': 'DC', -}; - -/** - * Quick task 260630-hct -- normalise a deriveRegion() result into a compact, - * state-granularity STRING label for storage (never the raw IP). - * - * { country: 'US', subdivision: 'California' } -> 'US-CA' - * { country: 'AU', subdivision: 'Victoria' } -> 'AU-Victoria' (slugged) - * 'unknown' / missing country -> 'unknown' - * - * @param {{country?:string, subdivision?:string}|string} region - * @returns {string} - */ -function regionLabel(region) { - if (!region || typeof region !== 'object' || typeof region.country !== 'string' || region.country === '') { - return 'unknown'; - } - const country = region.country.trim().toUpperCase().slice(0, 8); - const sub = typeof region.subdivision === 'string' ? region.subdivision.trim() : ''; - if (sub === '') return country; - if (country === 'US' && US_STATE_CODES[sub]) { - return `US-${US_STATE_CODES[sub]}`; - } - // Generic compact slug for non-US (or unknown US) subdivisions: collapse - // whitespace to single hyphens and cap length so labels stay bounded. - const slug = sub.replace(/\s+/g, '-').slice(0, 24); - return `${country}-${slug}`; -} - /** * Validate one event against the strict allowlist + shape rules. * Returns null on success, { error, field? } on failure. @@ -216,7 +172,7 @@ function createTelemetryRouter(db, queries, hashIp) { // the plaintext client IP is referenced EXACTLY THREE times per request, on // the next three lines, via clientIp(req) (Fly-Client-IP, else req.ip): // 1. hashIp(ipKeyGenerator(clientIp(req)), db) -- rate-limit/HMAC hash - // 2. deriveRegion(clientIp(req)) -- coarse country/US-state geo + // 2. deriveRegion(clientIp(req)) -- coarse city/state/country geo // 3. classifyIp(clientIp(req)) -- address family enum, never the IP // All three references are inline arguments to an immediately-evaluated // call. The IP is NEVER assigned to a local that escapes this scope, NEVER @@ -247,8 +203,8 @@ function createTelemetryRouter(db, queries, hashIp) { const clientHash = hashIp(ipKeyGenerator(clientIp(req)), db); // Second inline client-IP touch: coarse geo derive. Pass the raw client // address, not the rate-limit key. deriveRegion unwraps IPv4-mapped IPv6, - // returns {country, subdivision} | 'unknown'; regionLabel() collapses it to - // a compact state-granularity STRING (e.g. 'US-CA' or 'unknown'). + // returns {country, subdivision, city} | 'unknown'; regionLabel() collapses + // it to a compact place STRING (e.g. 'US-CA/San Jose', 'US-CA', 'unknown'). const regionTag = regionLabel(deriveRegion(clientIp(req))); // Third inline client-IP touch: address-family enum only (never the IP). // Persisted beside region so 'unknown' is diagnosable after 7-day event @@ -355,7 +311,7 @@ function createTelemetryRouter(db, queries, hashIp) { let n = 0; for (const e of rows) { // Quick task 260630-hct -- 12-arg region-bearing insert. The trailing - // regionTag is a coarse state-granularity label derived inline above, + // regionTag is a coarse city-granularity label derived inline above, // NEVER the raw IP. All other args + batching/budget logic unchanged. const activeCountVersion = e.active_count_version === ACTIVE_COUNT_VERSION ? ACTIVE_COUNT_VERSION : 0; diff --git a/showcase/server/src/telemetry/housekeeper.js b/showcase/server/src/telemetry/housekeeper.js index 5a765fd1..e467d189 100644 --- a/showcase/server/src/telemetry/housekeeper.js +++ b/showcase/server/src/telemetry/housekeeper.js @@ -38,6 +38,7 @@ const Queries = require('../db/queries'); const { hashIp } = require('../utils/telemetry-hash'); +const { regionDepth, regionParent } = require('../utils/region-label'); const ONE_HOUR_MS = 60 * 60 * 1000; const ONE_DAY_MS = 24 * 60 * 60 * 1000; @@ -47,37 +48,70 @@ const ACTIVE_COUNT_VERSION = 2; const K_ANONYMITY_FLOOR = 1; // Quick task 260630-hct -- region anonymity floor. HARD-required at k>=5 by // CONTEXT (do NOT reuse the relaxed K_ANONYMITY_FLOOR=1 used for mcp_client). -// Regions with fewer than 5 unique installs collapse into a single 'Other' -// bucket; that bucket is suppressed entirely when its summed install count is -// itself < 5. This guarantees no surfaced region label represents < 5 installs. +// No published region label -- city, subdivision, country, or 'Other' -- ever +// represents fewer than 5 unique installs; see applyRegionKFloor. const REGION_K_FLOOR = 5; /** - * Apply a k-floor using distinct membership, not a sum of per-label counts. - * One install can move between regions in a day; it must count once in the - * combined Other bucket even when it appears under several sub-floor labels. + * Publish each install at the most specific place that clears the k-floor. + * + * Labels nest city -> subdivision -> country (region-label.js). Working from + * the finest level up, a label with >= `floor` installs is published and the + * installs of one below the floor move to its parent: 'US-CA/Fresno' (2) joins + * 'US-CA', which may then clear the floor on its own. Top-level labels still + * short (countries, 'unknown') pool into 'Other', which is itself dropped when + * under the floor. Every install lands in exactly one bucket, so the published + * counts are disjoint and each is >= `floor`. + * + * An install that appears under several labels keeps the first; the callers' + * queries already return one row per install. + * + * @param {Array<{region:string}>} rows + * @param {string} memberKey install id field on each row + * @param {number} floor + * @returns {Array<{region:string, uniq:number}>} published labels by size, 'Other' last */ -function applyDistinctKFloor(rows, key, memberKey, floor) { - const memberships = new Map(); +function applyRegionKFloor(rows, memberKey, floor) { + const pools = new Map(); + const addMember = (label, member) => { + let members = pools.get(label); + if (!members) { members = new Set(); pools.set(label, members); } + members.add(member); + }; + + const placed = new Set(); for (const row of rows) { - if (!row || typeof row[key] !== 'string' || typeof row[memberKey] !== 'string') continue; - if (!memberships.has(row[key])) memberships.set(row[key], new Set()); - memberships.get(row[key]).add(row[memberKey]); + if (!row || typeof row.region !== 'string' || row.region === '') continue; + const member = row[memberKey]; + if (typeof member !== 'string' || placed.has(member)) continue; + placed.add(member); + addMember(row.region, member); } - const above = []; - const belowMembers = new Set(); - for (const [label, members] of memberships) { - if (members.size >= floor) { - above.push({ [key]: label, uniq: members.size }); - } else { - for (const member of members) belowMembers.add(member); + const published = []; + const other = new Set(); + let depth = 0; + for (const label of pools.keys()) depth = Math.max(depth, regionDepth(label)); + for (; depth >= 0; depth--) { + for (const [label, members] of [...pools]) { + if (regionDepth(label) !== depth) continue; + pools.delete(label); + if (members.size >= floor) { + published.push({ region: label, uniq: members.size }); + continue; + } + const parent = regionParent(label); + for (const member of members) { + if (parent === null) other.add(member); + else addMember(parent, member); + } } } - above.sort((a, b) => b.uniq - a.uniq || a[key].localeCompare(b[key])); - return belowMembers.size >= floor - ? [...above, { [key]: 'Other', uniq: belowMembers.size }] - : above; + + published.sort((a, b) => b.uniq - a.uniq || a.region.localeCompare(b.region)); + return other.size >= floor + ? [...published, { region: 'Other', uniq: other.size }] + : published; } function floorToUtcDayMs(ms) { @@ -178,13 +212,8 @@ function runHousekeeperTick(db, queries, nowMs = Date.now()) { // Region rollup uses each install's latest membership for the day. // Assigning one region before applying the floor prevents a roaming - // install from appearing in both a named region and the Other bucket. - const popularRegion = applyDistinctKFloor( - regionMemberships, - 'region', - 'install_uuid', - REGION_K_FLOOR - ); + // install from being counted in two published places. + const popularRegion = applyRegionKFloor(regionMemberships, 'install_uuid', REGION_K_FLOOR); queries.upsertGlobalAggregateV2.run( dayKey, @@ -254,7 +283,7 @@ module.exports = { startHousekeeper, runHousekeeperTick, floorToUtcDayMs, - applyDistinctKFloor, + applyRegionKFloor, K_ANONYMITY_FLOOR, REGION_K_FLOOR, ROLLUP_RETENTION_DAYS, diff --git a/showcase/server/src/utils/ip-geo.js b/showcase/server/src/utils/ip-geo.js index a8c810a1..2f51d083 100644 --- a/showcase/server/src/utils/ip-geo.js +++ b/showcase/server/src/utils/ip-geo.js @@ -1,6 +1,6 @@ /** - * Quick task 260630-hct -- coarse, self-hosted IP -> region (country + subdivision) - * lookup for anonymous aggregate telemetry. + * Coarse, self-hosted IP -> place (country, subdivision, city) lookup for + * anonymous aggregate telemetry. * * PRIVACY INVARIANT (mirrors src/utils/telemetry-hash.js posture): * - `ip` is accepted as a function argument @@ -16,61 +16,39 @@ * (that would leak every user's raw IP to a third party, which is worse than * the existing hashed-IP posture). * - * WORLDWIDE / 256 MB MEMORY MODEL: - * The production dataset is WORLDWIDE (all countries, IPv4 + IPv6) and can be a - * few hundred thousand to a few million ranges. To stay within the 256 MB Fly - * VM (binding limit is process RSS) the parsed table is a struct-of-arrays: + * SEARCHED ON DISK, NOT LOADED: + * City-level worldwide tables run to millions of ranges (~3.4 M IPv4, ~3.5 M + * IPv6 /64 prefixes) -- about 110 MB as typed arrays, which does not fit + * beside the server on the 256 MB Fly VM. So nothing is parsed into memory: + * each file stays sorted on disk and every lookup binary-searches it with + * positioned reads (~16 reads of 4 KiB per lookup, served from the page cache + * once warm). The only per-file allocation is one 64 KiB read buffer, and + * there is no load pause on the first request. * - * _table = { - * n, // number of ranges - * starts: Uint32Array(n), // inclusive uint32 IPv4 range starts (sorted asc) - * ends: Uint32Array(n), // inclusive uint32 IPv4 range ends - * labelId: Uint32Array(n), // index into `labels` (deduped/interned) - * labels: Array<{country, subdivision}>, // interned; index === labelId - * } + * Three files, all sorted ascending and '#'/blank-line tolerant: + * IPv4 start_ip_int,end_ip_int,country,subdivision,city + * IPv6 start64,end64,country,subdivision,city (/64 prefixes, 16 hex digits) + * places label,lat,lon (sorted by label) + * Four-column range rows from the older state-level files still parse, with + * an empty city. The places file maps every region label the refresh script + * can produce (country, subdivision, and city level; see region-label.js) to + * an approximate centroid, so public stats can position a published place + * without anything location-shaped being stored per install. * - * Retained cost is ~12 bytes/range (3 x Uint32) + a tiny interned-label set - * (a few thousand distinct {country,subdivision} pairs worldwide). The file is - * read in 1 MB CHUNKS (never a whole-file Buffer or string) and scanned - * BYTE-BY-BYTE: integers are parsed directly from ASCII digits; only the short - * country/subdivision byte-ranges are decoded to strings, then interned. A - * first pass counts newlines for an exact typed-array allocation; a second - * pass fills, rewinding the file position to each chunk's last complete line so - * no partial line straddles a chunk. Working set during load is ~1 MB + the - * final typed arrays -- so a ~70 MB / 2 M-range worldwide file loads with a - * ~25-30 MB footprint (NOT a ~100 MB whole-file spike). This deliberately - * avoids the original object-array loader's blow-up AND three whole-file - * string hazards: a multi-MB transient string, V8 silently widening it to - * UTF-16 the moment a non-Latin1 subdivision name (CJK/Cyrillic) appears, and - * V8's ~512 MiB string-length cap. + * IPv4-mapped IPv6 (`::ffff:a.b.c.d`) unwraps to IPv4; CIDR `/56` keys do not + * parse and stay 'unknown'. DB-IP labels private/reserved space (10/8, 127/8, + * ::1, fe80::, Fly 6PN fdaa::) as country 'ZZ'; that is reported as 'unknown'. * - * The dataset is emitted PRE-SORTED by scripts/refresh-dbip-dataset.mjs, so - * there is NO load-time sort; the binary search relies on that ordering (the - * committed fixture is likewise kept sorted ascending by start_ip_int). + * GRACEFUL DEGRADATION (HARD requirement): if a dataset file is absent, + * unreadable, or empty, its lookups return 'unknown' / null and this module + * NEVER throws. The server boots and ingests with no dataset present. The real + * artifacts are generated by scripts/refresh-dbip-dataset.mjs and are NOT + * committed; only tiny fixtures ship for tests. A file replaced in place is not + * noticed until restart -- the open descriptor keeps reading the old inode. * - * IPv6 uses a sibling table keyed on the top 64 bits of the address (the /64 - * prefix; DB-IP carries no location below it). Each bound is two uint32 words, - * so the table is 5 x Uint32 = 20 bytes/range (~42 MB for the ~2.2 M-range - * worldwide file) and loads without BigInt. It is read the same chunked way - * from DBIP_IPV6_DATASET_PATH (or a sibling of the IPv4 path: `*.ipv6.csv` / - * `*.ipv6.fixture.csv`). Native IPv6 (Jio / 2405:201:: and similar) looks up - * here; CIDR `/56` keys do not parse and stay 'unknown'. IPv4-mapped IPv6 - * (`::ffff:a.b.c.d`) still unwraps to IPv4. - * - * DB-IP labels private/reserved space (10/8, 127/8, ::1, fe80::, Fly 6PN - * fdaa::) as country 'ZZ'; both tables report that as 'unknown', never as a - * region. - * - * GRACEFUL DEGRADATION (HARD requirement): if the production dataset file is - * absent, unreadable, empty, or pathologically large (Buffer/file size limits), - * every call returns the literal string 'unknown' and this module NEVER throws. - * The server boots and ingests with no dataset present. The real artifact is - * generated by scripts/refresh-dbip-dataset.mjs and is NOT committed; only tiny - * fixtures (data/dbip-city-lite.fixture.csv and .ipv6.fixture.csv) ship for tests. - * - * Dataset path resolution: process.env.DBIP_DATASET_PATH || the default - * production artifact path under showcase/server/data/. IPv6: DBIP_IPV6_DATASET_PATH - * or a sibling of the IPv4 path. + * Dataset paths: DBIP_DATASET_PATH (IPv4), DBIP_IPV6_DATASET_PATH and + * DBIP_PLACES_DATASET_PATH, the latter two defaulting to siblings of the IPv4 + * path (`*.ipv6.csv`, `*.places.csv`, or the `.fixture.csv` forms). * * tests/server-ip-geo.test.js exercises hit / miss / absent-dataset / malformed; * tests/server-ip-geo-scale.test.js exercises a large synthetic dataset. @@ -81,33 +59,53 @@ const fs = require('fs'); const path = require('path'); -// Default production artifact path. The refresh script writes here; .gitignore -// excludes it (but keeps *.fixture.csv). Overridable via DBIP_DATASET_PATH. +// Default production artifact paths. The refresh script writes here; .gitignore +// excludes them (but keeps *.fixture.csv). const DEFAULT_DATASET_PATH = path.join(__dirname, '..', '..', 'data', 'dbip-city-lite.csv'); const DEFAULT_IPV6_DATASET_PATH = path.join(__dirname, '..', '..', 'data', 'dbip-city-lite.ipv6.csv'); - -// Lazily-populated parsed tables. Three states each: -// undefined -> not yet loaded (load on first call) -// null -> load attempted and failed/absent/empty (graceful 'unknown') -// object -> struct-of-arrays (see header) -let _table; -let _ipv6Table; - -/** Sibling IPv6 artifact for an IPv4 compact-CSV path. */ -function siblingIpv6Path(ipv4Path) { - if (typeof ipv4Path !== 'string' || ipv4Path === '') return DEFAULT_IPV6_DATASET_PATH; +const DEFAULT_PLACES_DATASET_PATH = path.join(__dirname, '..', '..', 'data', 'dbip-city-lite.places.csv'); + +// Lazily-opened dataset handles. Three states each: +// undefined -> not opened yet (open on first lookup) +// null -> absent / unreadable / empty (graceful 'unknown') +// object -> { fd, size, buf } +let _ipv4File; +let _ipv6File; +let _placesFile; + +/** Sibling artifact for an IPv4 compact-CSV path: `kind` is 'ipv6' or 'places'. */ +function siblingPath(ipv4Path, kind, fallback) { + if (typeof ipv4Path !== 'string' || ipv4Path === '') return fallback; if (ipv4Path.endsWith('.fixture.csv')) { - return ipv4Path.replace(/\.fixture\.csv$/, '.ipv6.fixture.csv'); + return ipv4Path.replace(/\.fixture\.csv$/, `.${kind}.fixture.csv`); } - if (ipv4Path.endsWith('.csv')) return ipv4Path.slice(0, -4) + '.ipv6.csv'; - return ipv4Path + '.ipv6'; + if (ipv4Path.endsWith('.csv')) return ipv4Path.slice(0, -4) + `.${kind}.csv`; + return `${ipv4Path}.${kind}`; +} + +function siblingIpv6Path(ipv4Path) { + return siblingPath(ipv4Path, 'ipv6', DEFAULT_IPV6_DATASET_PATH); +} + +function siblingPlacesPath(ipv4Path) { + return siblingPath(ipv4Path, 'places', DEFAULT_PLACES_DATASET_PATH); +} + +function resolveIpv4DatasetPath() { + return process.env.DBIP_DATASET_PATH || DEFAULT_DATASET_PATH; } function resolveIpv6DatasetPath() { if (process.env.DBIP_IPV6_DATASET_PATH) return process.env.DBIP_IPV6_DATASET_PATH; - return siblingIpv6Path(process.env.DBIP_DATASET_PATH || DEFAULT_DATASET_PATH); + return siblingIpv6Path(resolveIpv4DatasetPath()); +} + +function resolvePlacesDatasetPath() { + if (process.env.DBIP_PLACES_DATASET_PATH) return process.env.DBIP_PLACES_DATASET_PATH; + return siblingPlacesPath(resolveIpv4DatasetPath()); } + /** * If `ip` is an IPv4-mapped IPv6 address, return the dotted-quad IPv4 form. * Node and Fly both emit `::ffff:a.b.c.d` (and occasionally `:ffff:a.b.c.d`) @@ -263,71 +261,48 @@ function _parseHex32(buf, p, end) { return n; } -/** - * Parse ONE compact-CSV line, given as the byte-range buf[start, end), into the - * mutable accumulator `acc` ({ starts, ends, labelId, labels, labelMap, w }). - * Line shape: `start_ip_int,end_ip_int,country,subdivision`. Blank/'#'/malformed - * lines are silently skipped. Integers are parsed straight from ASCII digits - * (zero allocation); only the short country/subdivision byte-ranges are decoded - * to strings, then interned. Byte constants: 10='\n' 13='\r' 44=',' 35='#' - * 32=' ' 9='\t' 48..57='0'..'9'. - * - * @param {Buffer} buf - * @param {number} start inclusive - * @param {number} end exclusive (caller has already excluded the newline byte) - * @param {{starts:Uint32Array,ends:Uint32Array,labelId:Uint32Array,labels:Array,labelMap:Map,w:number}} acc - */ -function _parseLineBytes(buf, start, end, acc) { - if (end > start && buf[end - 1] === 13) end--; // strip a trailing '\r' +// --------------------------------------------------------------------------- +// Sorted line files, searched in place. +// --------------------------------------------------------------------------- - let p = start; - while (p < end && (buf[p] === 32 || buf[p] === 9)) p++; // skip leading ws - if (p >= end || buf[p] === 35) return; // blank / comment +// Read size for each probe. Data lines are well under 200 bytes, so one read +// nearly always holds the line after the probe point. +const WINDOW_BYTES = 4096; +// A line longer than this is treated as corrupt rather than read further. +const MAX_LINE_BYTES = 64 * 1024; - // start_ip_int (ASCII digits). - let startInt = 0; let sawStart = false; - while (p < end && buf[p] >= 48 && buf[p] <= 57) { startInt = startInt * 10 + (buf[p] - 48); p++; sawStart = true; } - if (!sawStart || p >= end || buf[p] !== 44) return; - p++; +const STOP = Symbol('stop'); - // end_ip_int (ASCII digits). - let endInt = 0; let sawEnd = false; - while (p < end && buf[p] >= 48 && buf[p] <= 57) { endInt = endInt * 10 + (buf[p] - 48); p++; sawEnd = true; } - if (!sawEnd || p >= end || buf[p] !== 44) return; - p++; +function openSortedFile(filePath) { + let fd = null; + try { + fd = fs.openSync(filePath, 'r'); + const { size } = fs.fstatSync(fd); + if (size > 0) return { fd, size, buf: Buffer.allocUnsafe(MAX_LINE_BYTES) }; + } catch { + // Absent / unreadable: fall through to the graceful null below. + } + if (fd !== null) { try { fs.closeSync(fd); } catch { /* ignore */ } } + return null; +} - // country: bytes up to the next ','. - const cStart = p; - while (p < end && buf[p] !== 44) p++; - if (p >= end) return; // missing 3rd comma - const cEnd = p; - p++; +function closeSortedFile(file) { + if (file) { try { fs.closeSync(file.fd); } catch { /* ignore */ } } +} - // subdivision: the remainder of the line (kept whole even if it contains - // commas -- though the transform strips commas from labels). - const sStart = p; - const sEnd = end; - - if (endInt < startInt) return; - if (cEnd <= cStart) return; // empty country - - // Decode ONLY the short label byte-ranges to strings, then intern. - const country = buf.toString('utf8', cStart, cEnd).trim(); - if (country === '') return; - const subdivision = buf.toString('utf8', sStart, sEnd).trim(); - - const key = country + '\x1F' + subdivision; - let id = acc.labelMap.get(key); - if (id === undefined) { - id = acc.labels.length; - acc.labels.push({ country, subdivision }); - acc.labelMap.set(key, id); - } +function ipv4File() { + if (_ipv4File === undefined) _ipv4File = openSortedFile(resolveIpv4DatasetPath()); + return _ipv4File; +} + +function ipv6File() { + if (_ipv6File === undefined) _ipv6File = openSortedFile(resolveIpv6DatasetPath()); + return _ipv6File; +} - acc.starts[acc.w] = startInt >>> 0; - acc.ends[acc.w] = endInt >>> 0; - acc.labelId[acc.w] = id; - acc.w += 1; +function placesFile() { + if (_placesFile === undefined) _placesFile = openSortedFile(resolvePlacesDatasetPath()); + return _placesFile; } // Next newline (byte 10) in buf within [from, limit), or -1. The read buffer is @@ -337,327 +312,310 @@ function _indexOfNewline(buf, from, limit) { return (nl < 0 || nl >= limit) ? -1 : nl; } -// Read chunk size for the two-pass file scan. 1 MiB keeps the load working set -// tiny regardless of dataset size; lines are < ~64 bytes so one chunk holds many. -const LOAD_CHUNK_BYTES = 1 << 20; - /** - * Lazy, once-only dataset load. Resolves the path from DBIP_DATASET_PATH or the - * default, then reads the file in 1 MiB CHUNKS (never a whole-file Buffer/string): - * - pass 1 counts newline bytes for an exact typed-array allocation; - * - pass 2 fills the arrays, rewinding the file position to each chunk's last - * complete line so no partial line straddles a chunk boundary. - * On ANY failure (absent / unreadable / empty / parse-empty / oversized) caches - * `null` so subsequent calls do not re-stat the filesystem and the module - * degrades to 'unknown' without throwing. - * - * @returns {{n:number, starts:Uint32Array, ends:Uint32Array, labelId:Uint32Array, labels:Array}|null} + * Call visit(buf, start, end, next) for each complete line whose first byte + * lies in [from, limit), in file order. A line that began before `from` is + * skipped: it belongs to the previous probe. `end` excludes the newline and a + * trailing '\r'; `next` is the file offset of the following line. The first + * non-undefined visit() result is returned. */ -function loadTable() { - if (_table !== undefined) return _table; - const datasetPath = process.env.DBIP_DATASET_PATH || DEFAULT_DATASET_PATH; - let fd = null; - try { - fd = fs.openSync(datasetPath, 'r'); - const buf = Buffer.allocUnsafe(LOAD_CHUNK_BYTES); - - // Pass 1: count newline bytes -> safe upper bound on row count. - // maxRows = newlines + 1 covers a final line with no trailing newline; it - // also over-counts blank/comment lines, which subarray() trims off later. - let newlineCount = 0; - let total = 0; - let pos = 0; +function forEachLine(file, from, limit, visit) { + const { fd, size, buf } = file; + let pos = from; + + if (pos > 0) { + // Skip to just past the first newline at or after from-1. + pos -= 1; for (;;) { - const n = fs.readSync(fd, buf, 0, LOAD_CHUNK_BYTES, pos); - if (n === 0) break; + if (pos >= size) return undefined; + const n = fs.readSync(fd, buf, 0, WINDOW_BYTES, pos); + if (n <= 0) return undefined; + const nl = _indexOfNewline(buf, 0, n); + if (nl !== -1) { pos += nl + 1; break; } pos += n; - total += n; - for (let i = 0; i < n; i++) if (buf[i] === 10) newlineCount++; - } - if (total === 0) { _table = null; return _table; } - - const maxRows = newlineCount + 1; - const acc = { - starts: new Uint32Array(maxRows), - ends: new Uint32Array(maxRows), - labelId: new Uint32Array(maxRows), - labels: [], - labelMap: new Map(), - w: 0, - }; - - // Pass 2: parse only COMPLETE lines per chunk; rewind to the byte after the - // last newline so the trailing partial line is re-read at the head of the - // next chunk (no cross-chunk carry buffer needed). - pos = 0; - for (;;) { - const n = fs.readSync(fd, buf, 0, LOAD_CHUNK_BYTES, pos); - if (n === 0) break; - - let lastNL = -1; - for (let i = n - 1; i >= 0; i--) { if (buf[i] === 10) { lastNL = i; break; } } - - let limit; - if (lastNL === -1) { - // No newline in a full chunk => a single line longer than the chunk. - // Our rows are tiny so this is unreachable; guard against an infinite - // loop by consuming the chunk as one (likely-malformed) line. - limit = n; - pos += n; - } else { - limit = lastNL; // parse complete lines in [0, lastNL) - pos += lastNL + 1; // next read resumes just past that newline - } - - let i = 0; - while (i < limit) { - let nl = _indexOfNewline(buf, i, limit); - if (nl === -1) nl = limit; // last complete line ends exactly at `limit` - _parseLineBytes(buf, i, nl, acc); - i = nl + 1; - } } + } - if (acc.w === 0) { _table = null; return _table; } - _table = { - n: acc.w, - starts: acc.starts.subarray(0, acc.w), - ends: acc.ends.subarray(0, acc.w), - labelId: acc.labelId.subarray(0, acc.w), - labels: acc.labels, - }; - } catch { - // Absent / unreadable / oversized -> graceful degradation. Nothing sensitive - // logged. Any chunk buffer is GC'd. - _table = null; - } finally { - if (fd !== null) { try { fs.closeSync(fd); } catch { /* ignore */ } } + let window = WINDOW_BYTES; + while (pos < limit && pos < size) { + const n = fs.readSync(fd, buf, 0, window, pos); + if (n <= 0) return undefined; + let i = 0; + while (i < n && pos + i < limit) { + const nl = _indexOfNewline(buf, i, n); + if (nl === -1 && pos + n < size) break; // line continues past this read + const lineEnd = nl === -1 ? n : nl; + const end = lineEnd > i && buf[lineEnd - 1] === 13 ? lineEnd - 1 : lineEnd; + const result = visit(buf, i, end, pos + lineEnd + 1); + if (result !== undefined) return result; + if (nl === -1) return undefined; // last line, no trailing newline + i = nl + 1; + } + if (pos + i >= limit) return undefined; + if (i === 0) { + // One line fills the whole read: widen it, or give up on a corrupt line. + if (window >= MAX_LINE_BYTES) return undefined; + window = Math.min(window * 2, MAX_LINE_BYTES); + } + pos += i; } - return _table; + return undefined; } /** - * Parse ONE compact IPv6 CSV line: start64,end64,country,subdivision where each - * bound is exactly 16 hex digits (the inclusive /64 prefix). Blank, '#', and - * malformed lines -- including the old decimal-halves format -- are skipped. - * - * @param {Buffer} buf - * @param {number} start - * @param {number} end - * @param {{startHi:Uint32Array,startLo:Uint32Array,endHi:Uint32Array,endLo:Uint32Array,labelId:Uint32Array,labels:Array,labelMap:Map,w:number}} acc + * Binary-search a sorted line file. compare(buf, start, end) returns null for a + * line that is not data (header comment, blank, malformed), a negative number + * when the key sorts before the line, a positive one when after, and 0 on a + * match, which decode(buf, start, end) turns into the return value. Returns + * null when nothing matches. */ -function _parseIpv6LineBytes(buf, start, end, acc) { - if (end > start && buf[end - 1] === 13) end--; +function searchSortedFile(file, compare, decode) { + let lo = 0; + let hi = file.size; + // Invariant: a matching line, if any, starts in [lo, hi). + while (hi - lo > WINDOW_BYTES) { + const mid = lo + Math.floor((hi - lo) / 2); + let nextAfterProbe = -1; + const found = forEachLine(file, mid, hi, (buf, s, e, next) => { + const c = compare(buf, s, e); + if (c === null) return undefined; + if (c === 0) return decode(buf, s, e); + if (c > 0) nextAfterProbe = next; + return STOP; + }); + if (found !== undefined && found !== STOP) return found; + if (nextAfterProbe >= 0) lo = nextAfterProbe; + else hi = mid; // key sorts before the first data line at or after mid, or there is none + } + const found = forEachLine(file, lo, hi, (buf, s, e) => { + const c = compare(buf, s, e); + if (c === null || c > 0) return undefined; + return c === 0 ? decode(buf, s, e) : STOP; + }); + return found === undefined || found === STOP ? null : found; +} +// --------------------------------------------------------------------------- +// Range rows: bounds, then country,subdivision[,city]. Byte constants: +// 44=',' 35='#' 32=' ' 9='\t' 48..57='0'..'9'. +// --------------------------------------------------------------------------- + +/** Offset of the first non-blank byte, or -1 for a blank or '#' comment line. */ +function _dataStart(buf, start, end) { let p = start; while (p < end && (buf[p] === 32 || buf[p] === 9)) p++; - if (p >= end || buf[p] === 35) return; + return p >= end || buf[p] === 35 ? -1 : p; +} + +/** + * Offset just past the country field when buf[p..end) is `country,...` with a + * non-empty country, else -1. Older files end at the subdivision; newer ones + * add a city column. + */ +function _countryEnd(buf, p, end) { + const cStart = p; + while (p < end && buf[p] !== 44) p++; + return p > cStart && p < end ? p : -1; +} + +function _decodePlaceFields(buf, p, end) { + const cEnd = _countryEnd(buf, p, end); + const country = buf.toString('utf8', p, cEnd).trim(); + let q = cEnd + 1; + while (q < end && buf[q] !== 44) q++; + const subdivision = buf.toString('utf8', cEnd + 1, q).trim(); + const city = q < end ? buf.toString('utf8', q + 1, end).trim() : ''; + return { country, subdivision, city }; +} + +// DB-IP marks private/reserved space (RFC 1918, loopback, link-local, ULA incl. +// Fly 6PN) as country 'ZZ'. That is not a place, so it must not become a region. +const RESERVED_COUNTRY = 'ZZ'; + +function regionFromFields(fields) { + return fields.country === RESERVED_COUNTRY ? 'unknown' : fields; +} + +/** Parse an IPv4 row's bounds and label-field offset into `out`; false for a non-data line. */ +function _parseIpv4Bounds(buf, start, end, out) { + let p = _dataStart(buf, start, end); + if (p < 0) return false; + + let lo = 0; let sawLo = false; + while (p < end && buf[p] >= 48 && buf[p] <= 57) { lo = lo * 10 + (buf[p] - 48); p++; sawLo = true; } + if (!sawLo || p >= end || buf[p] !== 44) return false; + p++; + + let hi = 0; let sawHi = false; + while (p < end && buf[p] >= 48 && buf[p] <= 57) { hi = hi * 10 + (buf[p] - 48); p++; sawHi = true; } + if (!sawHi || p >= end || buf[p] !== 44) return false; + p++; + + if (hi < lo || lo > 0xFFFFFFFF || _countryEnd(buf, p, end) < 0) return false; + out.lo = lo; out.hi = hi; out.fields = p; + return true; +} + +function lookupIpv4(key) { + const file = ipv4File(); + if (!file) return 'unknown'; + const row = { lo: 0, hi: 0, fields: 0 }; + const found = searchSortedFile( + file, + (buf, s, e) => { + if (!_parseIpv4Bounds(buf, s, e, row)) return null; + if (key < row.lo) return -1; + return key > row.hi ? 1 : 0; + }, + (buf, s, e) => { + _parseIpv4Bounds(buf, s, e, row); + return regionFromFields(_decodePlaceFields(buf, row.fields, e)); + } + ); + return found || 'unknown'; +} + +/** Same for an IPv6 row, whose bounds are start64,end64 as exactly 16 hex digits each. */ +function _parseIpv6Bounds(buf, start, end, out) { + let p = _dataStart(buf, start, end); + if (p < 0) return false; const startHi = _parseHex32(buf, p, end); const startLo = _parseHex32(buf, p + 8, end); - if (startHi < 0 || startLo < 0 || p + 16 >= end || buf[p + 16] !== 44) return; + if (startHi < 0 || startLo < 0 || p + 16 >= end || buf[p + 16] !== 44) return false; p += 17; const endHi = _parseHex32(buf, p, end); const endLo = _parseHex32(buf, p + 8, end); - if (endHi < 0 || endLo < 0 || p + 16 >= end || buf[p + 16] !== 44) return; + if (endHi < 0 || endLo < 0 || p + 16 >= end || buf[p + 16] !== 44) return false; p += 17; - const cStart = p; - while (p < end && buf[p] !== 44) p++; - if (p >= end) return; - const cEnd = p; - p++; - - if (cmp64(endHi, endLo, startHi, startLo) < 0) return; - if (cEnd <= cStart) return; - - const country = buf.toString('utf8', cStart, cEnd).trim(); - if (country === '') return; - const subdivision = buf.toString('utf8', p, end).trim(); - - const key = country + '\x1F' + subdivision; - let id = acc.labelMap.get(key); - if (id === undefined) { - id = acc.labels.length; - acc.labels.push({ country, subdivision }); - acc.labelMap.set(key, id); - } + if (cmp64(endHi, endLo, startHi, startLo) < 0 || _countryEnd(buf, p, end) < 0) return false; + out.startHi = startHi; out.startLo = startLo; + out.endHi = endHi; out.endLo = endLo; + out.fields = p; + return true; +} - acc.startHi[acc.w] = startHi; - acc.startLo[acc.w] = startLo; - acc.endHi[acc.w] = endHi; - acc.endLo[acc.w] = endLo; - acc.labelId[acc.w] = id; - acc.w += 1; +function lookupIpv6(hi, lo) { + const file = ipv6File(); + if (!file) return 'unknown'; + const row = { startHi: 0, startLo: 0, endHi: 0, endLo: 0, fields: 0 }; + const found = searchSortedFile( + file, + (buf, s, e) => { + if (!_parseIpv6Bounds(buf, s, e, row)) return null; + if (cmp64(hi, lo, row.startHi, row.startLo) < 0) return -1; + return cmp64(hi, lo, row.endHi, row.endLo) > 0 ? 1 : 0; + }, + (buf, s, e) => { + _parseIpv6Bounds(buf, s, e, row); + return regionFromFields(_decodePlaceFields(buf, row.fields, e)); + } + ); + return found || 'unknown'; } /** - * Lazy, once-only IPv6 dataset load. Same chunked two-pass scan as loadTable. - * Path: DBIP_IPV6_DATASET_PATH or a sibling of the IPv4 compact CSV. + * Derive a coarse { country, subdivision, city } place from a plaintext IP, or + * the literal string 'unknown' when the IP falls outside every range, lands in + * DB-IP's reserved 'ZZ' space, or no dataset is present. `city` is '' when the + * dataset row has none (including the older state-level files). IPv4 + * (including mapped ::ffff:a.b.c.d) uses the IPv4 table; native IPv6 uses the + * sibling IPv6 table. NEVER throws; NEVER retains or logs the IP. * - * @returns {{n:number, startHi:Uint32Array, startLo:Uint32Array, endHi:Uint32Array, endLo:Uint32Array, labelId:Uint32Array, labels:Array}|null} + * @param {string} ip plaintext request IP (Fly-Client-IP / req.ip) + * @returns {{country:string, subdivision:string, city:string}|'unknown'} */ -function loadIpv6Table() { - if (_ipv6Table !== undefined) return _ipv6Table; - const datasetPath = resolveIpv6DatasetPath(); - let fd = null; +function deriveRegion(ip) { try { - fd = fs.openSync(datasetPath, 'r'); - const buf = Buffer.allocUnsafe(LOAD_CHUNK_BYTES); - - let newlineCount = 0; - let total = 0; - let pos = 0; - for (;;) { - const n = fs.readSync(fd, buf, 0, LOAD_CHUNK_BYTES, pos); - if (n === 0) break; - pos += n; - total += n; - for (let i = 0; i < n; i++) if (buf[i] === 10) newlineCount++; - } - if (total === 0) { _ipv6Table = null; return _ipv6Table; } - - const maxRows = newlineCount + 1; - const acc = { - startHi: new Uint32Array(maxRows), - startLo: new Uint32Array(maxRows), - endHi: new Uint32Array(maxRows), - endLo: new Uint32Array(maxRows), - labelId: new Uint32Array(maxRows), - labels: [], - labelMap: new Map(), - w: 0, - }; - - pos = 0; - for (;;) { - const n = fs.readSync(fd, buf, 0, LOAD_CHUNK_BYTES, pos); - if (n === 0) break; - - let lastNL = -1; - for (let i = n - 1; i >= 0; i--) { if (buf[i] === 10) { lastNL = i; break; } } - - let limit; - if (lastNL === -1) { - limit = n; - pos += n; - } else { - limit = lastNL; - pos += lastNL + 1; - } - - let i = 0; - while (i < limit) { - let nl = _indexOfNewline(buf, i, limit); - if (nl === -1) nl = limit; - _parseIpv6LineBytes(buf, i, nl, acc); - i = nl + 1; - } - } - - if (acc.w === 0) { _ipv6Table = null; return _ipv6Table; } - _ipv6Table = { - n: acc.w, - startHi: acc.startHi.subarray(0, acc.w), - startLo: acc.startLo.subarray(0, acc.w), - endHi: acc.endHi.subarray(0, acc.w), - endLo: acc.endLo.subarray(0, acc.w), - labelId: acc.labelId.subarray(0, acc.w), - labels: acc.labels, - }; + const key = ipv4ToInt(ip); + if (key !== null) return lookupIpv4(key); + const prefix = ipv6ToPrefix64(ip); + return prefix ? lookupIpv6(prefix.hi, prefix.lo) : 'unknown'; } catch { - _ipv6Table = null; - } finally { - if (fd !== null) { try { fs.closeSync(fd); } catch { /* ignore */ } } + return 'unknown'; } - return _ipv6Table; } -// DB-IP marks private/reserved space (RFC 1918, loopback, link-local, ULA incl. -// Fly 6PN) as country 'ZZ'. That is not a place, so it must not become a region. -const RESERVED_COUNTRY = 'ZZ'; - -// Return a FRESH object so callers cannot mutate the interned label. -function regionFromLabel(lab) { - if (lab.country === RESERVED_COUNTRY) return 'unknown'; - return { country: lab.country, subdivision: lab.subdivision }; +// --------------------------------------------------------------------------- +// Place centroids: label,lat,lon sorted by label. +// --------------------------------------------------------------------------- + +/** Byte offset of the comma ending the label, or -1 for a non-data line. */ +function _placeLabelEnd(buf, start, end) { + const p = _dataStart(buf, start, end); + if (p < 0) return -1; + let q = p; + while (q < end && buf[q] !== 44) q++; + return q > p && q < end ? q : -1; } -function lookupIpv6(hi, lo) { - const t = loadIpv6Table(); - if (!t || t.n === 0) return 'unknown'; - - let left = 0; - let right = t.n - 1; - while (left <= right) { - const mid = (left + right) >>> 1; - if (cmp64(hi, lo, t.startHi[mid], t.startLo[mid]) < 0) { - right = mid - 1; - } else if (cmp64(hi, lo, t.endHi[mid], t.endLo[mid]) > 0) { - left = mid + 1; - } else { - return regionFromLabel(t.labels[t.labelId[mid]]); - } - } - return 'unknown'; +function _decodeCentroid(buf, labelEnd, end) { + let q = labelEnd + 1; + while (q < end && buf[q] !== 44) q++; + if (q >= end) return null; + const lat = Number(buf.toString('latin1', labelEnd + 1, q)); + const lon = Number(buf.toString('latin1', q + 1, end)); + if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null; + if (lat < -90 || lat > 90 || lon < -180 || lon > 180) return null; + return { lat, lon }; } /** - * Derive a coarse { country, subdivision } region from a plaintext IP, or the - * literal string 'unknown' when the IP falls outside every range, lands in - * DB-IP's reserved 'ZZ' space, or no dataset is loaded. IPv4 (including mapped ::ffff:a.b.c.d) uses the IPv4 table; native - * IPv6 uses the sibling IPv6 table. NEVER throws; NEVER retains or logs the IP. + * Approximate centroid of a region label ('US', 'US-CA', 'US-CA/San Jose'), or + * null when the label is not in the places file (or there is no file). This is + * where the place is, from the dataset -- never where an install is. * - * @param {string} ip plaintext request IP (Fly-Client-IP / req.ip) - * @returns {{country:string, subdivision:string}|'unknown'} + * @param {string} label + * @returns {{lat:number, lon:number}|null} */ -function deriveRegion(ip) { - const key = ipv4ToInt(ip); - if (key !== null) { - const t = loadTable(); - if (!t || t.n === 0) return 'unknown'; - - const { starts, ends, labelId, labels } = t; - - let lo = 0; - let hi = t.n - 1; - while (lo <= hi) { - const mid = (lo + hi) >>> 1; - if (key < starts[mid]) { - hi = mid - 1; - } else if (key > ends[mid]) { - lo = mid + 1; - } else { - return regionFromLabel(labels[labelId[mid]]); - } - } - return 'unknown'; +function placeCentroid(label) { + if (typeof label !== 'string' || label === '' || label === 'unknown' || label === 'Other') return null; + try { + const file = placesFile(); + if (!file) return null; + return searchSortedFile( + file, + (buf, s, e) => { + const labelEnd = _placeLabelEnd(buf, s, e); + if (labelEnd < 0) return null; + const lineLabel = buf.toString('utf8', _dataStart(buf, s, e), labelEnd); + if (label < lineLabel) return -1; + return label > lineLabel ? 1 : 0; + }, + (buf, s, e) => _decodeCentroid(buf, _placeLabelEnd(buf, s, e), e) + ); + } catch { + return null; } - - const prefix = ipv6ToPrefix64(ip); - if (!prefix) return 'unknown'; - return lookupIpv6(prefix.hi, prefix.lo); } /** - * Test-only: drop the cached tables so a test can re-point dataset paths and - * force a fresh load. Not used in production code paths. + * Test-only: close the cached files so a test can re-point dataset paths and + * force a fresh open. Not used in production code paths. */ function _resetForTest() { - _table = undefined; - _ipv6Table = undefined; + closeSortedFile(_ipv4File); + closeSortedFile(_ipv6File); + closeSortedFile(_placesFile); + _ipv4File = undefined; + _ipv6File = undefined; + _placesFile = undefined; } module.exports = { deriveRegion, + placeCentroid, ipv4ToInt, ipv6ToPrefix64, unwrapIpv4Mapped, classifyIp, GEO_KIND, siblingIpv6Path, + siblingPlacesPath, _resetForTest, }; module.exports.DEFAULT_DATASET_PATH = DEFAULT_DATASET_PATH; module.exports.DEFAULT_IPV6_DATASET_PATH = DEFAULT_IPV6_DATASET_PATH; +module.exports.DEFAULT_PLACES_DATASET_PATH = DEFAULT_PLACES_DATASET_PATH; diff --git a/showcase/server/src/utils/region-label.js b/showcase/server/src/utils/region-label.js new file mode 100644 index 00000000..9cb83825 --- /dev/null +++ b/showcase/server/src/utils/region-label.js @@ -0,0 +1,95 @@ +/** + * Compact place labels for anonymous region telemetry, and the hierarchy the + * k>=5 publication floor walks. + * + * { country: 'US', subdivision: 'California', city: 'San Jose' } -> 'US-CA/San Jose' + * { country: 'US', subdivision: 'California' } -> 'US-CA' + * { country: 'AU', subdivision: 'New South Wales' } -> 'AU-New-South-Wales' + * { country: 'SG', subdivision: '', city: 'Singapore' } -> 'SG/Singapore' + * { country: 'DE' } -> 'DE' + * 'unknown' / missing country -> 'unknown' + * + * '/' separates the city and '-' the subdivision, so both are stripped from the + * parts they would make ambiguous. The ingest route and the dataset refresh + * script share this module: the refresh script keys each place centroid on the + * same label the route stores, which is how public stats find a city's globe + * position without storing coordinates per install. + */ + +'use strict'; + +// US state name -> USPS 2-letter code, so US labels read 'US-CA' rather than a +// free-form state string. +const US_STATE_CODES = { + 'Alabama': 'AL', 'Alaska': 'AK', 'Arizona': 'AZ', 'Arkansas': 'AR', + 'California': 'CA', 'Colorado': 'CO', 'Connecticut': 'CT', 'Delaware': 'DE', + 'Florida': 'FL', 'Georgia': 'GA', 'Hawaii': 'HI', 'Idaho': 'ID', + 'Illinois': 'IL', 'Indiana': 'IN', 'Iowa': 'IA', 'Kansas': 'KS', + 'Kentucky': 'KY', 'Louisiana': 'LA', 'Maine': 'ME', 'Maryland': 'MD', + 'Massachusetts': 'MA', 'Michigan': 'MI', 'Minnesota': 'MN', 'Mississippi': 'MS', + 'Missouri': 'MO', 'Montana': 'MT', 'Nebraska': 'NE', 'Nevada': 'NV', + 'New Hampshire': 'NH', 'New Jersey': 'NJ', 'New Mexico': 'NM', 'New York': 'NY', + 'North Carolina': 'NC', 'North Dakota': 'ND', 'Ohio': 'OH', 'Oklahoma': 'OK', + 'Oregon': 'OR', 'Pennsylvania': 'PA', 'Rhode Island': 'RI', 'South Carolina': 'SC', + 'South Dakota': 'SD', 'Tennessee': 'TN', 'Texas': 'TX', 'Utah': 'UT', + 'Vermont': 'VT', 'Virginia': 'VA', 'Washington': 'WA', 'West Virginia': 'WV', + 'Wisconsin': 'WI', 'Wyoming': 'WY', 'District of Columbia': 'DC', +}; + +const MAX_SUBDIVISION_CHARS = 24; +const MAX_CITY_CHARS = 40; + +/** + * @param {{country?:string, subdivision?:string, city?:string}|string} region + * @returns {string} + */ +function regionLabel(region) { + if (!region || typeof region !== 'object' || typeof region.country !== 'string') { + return 'unknown'; + } + const country = region.country.trim().toUpperCase().replace(/[-/\s]/g, '').slice(0, 8); + if (country === '') return 'unknown'; + + const sub = typeof region.subdivision === 'string' ? region.subdivision.trim() : ''; + let label = country; + if (sub !== '') { + label = country === 'US' && US_STATE_CODES[sub] + ? `US-${US_STATE_CODES[sub]}` + : `${country}-${sub.replace(/[\s/]+/g, '-').slice(0, MAX_SUBDIVISION_CHARS)}`; + } + + const city = typeof region.city === 'string' + ? region.city.replace(/[\u0000-\u001f/]+/g, ' ').replace(/\s+/g, ' ').trim().slice(0, MAX_CITY_CHARS).trim() + : ''; + return city === '' ? label : `${label}/${city}`; +} + +/** + * The next-coarser label: city -> subdivision (or country when the city had no + * subdivision), subdivision -> country, country / 'unknown' / 'Other' -> null. + * + * @param {string} label + * @returns {string|null} + */ +function regionParent(label) { + if (typeof label !== 'string') return null; + const slash = label.indexOf('/'); + if (slash > 0) return label.slice(0, slash); + const dash = label.indexOf('-'); + if (dash > 0) return label.slice(0, dash); + return null; +} + +/** Number of coarser levels above `label` (city 2, subdivision 1, country 0). */ +function regionDepth(label) { + let depth = 0; + for (let p = regionParent(label); p !== null; p = regionParent(p)) depth++; + return depth; +} + +module.exports = { + regionLabel, + regionParent, + regionDepth, + US_STATE_CODES, +}; diff --git a/tests/server-client-ip-geo.test.js b/tests/server-client-ip-geo.test.js index f6591303..38b06257 100644 --- a/tests/server-client-ip-geo.test.js +++ b/tests/server-client-ip-geo.test.js @@ -4,7 +4,7 @@ * On Fly, trust proxy 1 resolves req.ip to the app anycast hop (SJC → US-CA) * or a 6PN IPv6 (unknown). This test sets X-Forwarded-For to a California-looking * address and Fly-Client-IP to the fixture's India range, then asserts the - * stored region is IN-Maharashtra and that two Fly-Client-IP values do not + * stored region is IN-Maharashtra/Mumbai and that two Fly-Client-IP values do not * share one ip_hash (the production 20/20 cap bug). * * Run: node tests/server-client-ip-geo.test.js @@ -91,7 +91,7 @@ function post(body, headers) { assert.strictEqual(clientIp({ get: () => ' ', ip: '8.8.8.8' }), '8.8.8.8'); const flyAnycast = '8.8.8.8'; // fixture US/California -- stands in for SJC - const indiaClient = '20.20.20.200'; // fixture IN/Maharashtra + const indiaClient = '20.20.20.200'; // fixture IN/Maharashtra/Mumbai const nyClient = '9.9.9.100'; // fixture US/New York const indiaRes = await post({ events: [event()] }, { @@ -110,11 +110,11 @@ function post(body, headers) { 'SELECT region, COUNT(DISTINCT ip_hash) AS hashes, COUNT(*) AS n FROM telemetry_events GROUP BY region ORDER BY region' ).all(); const byRegion = Object.fromEntries(rows.map((r) => [r.region, r])); - assert.ok(byRegion['IN-Maharashtra'], `expected IN-Maharashtra, got ${JSON.stringify(rows)}`); - assert.ok(byRegion['US-NY'], `expected US-NY, got ${JSON.stringify(rows)}`); - assert.strictEqual(byRegion['IN-Maharashtra'].n, 1); - assert.strictEqual(byRegion['US-NY'].n, 1); - assert.ok(!byRegion['US-CA'], `Fly anycast must not win geo, got ${JSON.stringify(rows)}`); + assert.ok(byRegion['IN-Maharashtra/Mumbai'], `expected IN-Maharashtra/Mumbai, got ${JSON.stringify(rows)}`); + assert.ok(byRegion['US-NY/New York'], `expected US-NY/New York, got ${JSON.stringify(rows)}`); + assert.strictEqual(byRegion['IN-Maharashtra/Mumbai'].n, 1); + assert.strictEqual(byRegion['US-NY/New York'].n, 1); + assert.ok(!rows.some((r) => r.region.startsWith('US-CA')), `Fly anycast must not win geo, got ${JSON.stringify(rows)}`); const hashCount = db.prepare('SELECT COUNT(DISTINCT ip_hash) AS c FROM telemetry_events').get().c; assert.strictEqual(hashCount, 2, `two client IPs must not collapse onto one hash, got ${hashCount}`); @@ -125,12 +125,12 @@ function post(body, headers) { }); assert.strictEqual(mappedRes.status, 200, mappedRes.body); const indiaCount = db.prepare( - "SELECT COUNT(*) AS n FROM telemetry_events WHERE region = 'IN-Maharashtra'" + "SELECT COUNT(*) AS n FROM telemetry_events WHERE region = 'IN-Maharashtra/Mumbai'" ).get().n; assert.strictEqual(indiaCount, 2, 'IPv4-mapped Fly-Client-IP must geolocate as India'); const kinds = db.prepare( - "SELECT DISTINCT geo_kind FROM telemetry_events WHERE region = 'IN-Maharashtra' ORDER BY 1" + "SELECT DISTINCT geo_kind FROM telemetry_events WHERE region = 'IN-Maharashtra/Mumbai' ORDER BY 1" ).all().map((r) => r.geo_kind); assert.deepStrictEqual(kinds, ['ipv4', 'ipv4-mapped']); @@ -143,9 +143,9 @@ function post(body, headers) { "SELECT region, geo_kind FROM telemetry_events WHERE geo_kind = 'ipv6'" ).get(); assert.ok(indiaIpv6, 'native IPv6 Fly-Client-IP must insert a row'); - assert.strictEqual(indiaIpv6.region, 'IN-Maharashtra'); + assert.strictEqual(indiaIpv6.region, 'IN-Maharashtra/Mumbai'); const indiaCountAfterV6 = db.prepare( - "SELECT COUNT(*) AS n FROM telemetry_events WHERE region = 'IN-Maharashtra'" + "SELECT COUNT(*) AS n FROM telemetry_events WHERE region = 'IN-Maharashtra/Mumbai'" ).get().n; assert.strictEqual(indiaCountAfterV6, 3, 'IPv4 + mapped + native IPv6 India'); diff --git a/tests/server-ip-geo-merge.test.js b/tests/server-ip-geo-merge.test.js index 61a0760b..b68cf27b 100644 --- a/tests/server-ip-geo-merge.test.js +++ b/tests/server-ip-geo-merge.test.js @@ -3,14 +3,16 @@ * * Runs the real transform script (via child_process) against a synthetic * upstream-DB-IP-shape CSV and asserts the merge pass: - * - consecutive rows with the SAME (country, subdivision) and contiguous IPv4 - * ranges collapse into ONE row spanning the full range; - * - a row with a DIFFERENT label stays separate; + * - consecutive rows with the SAME (country, subdivision, city) and contiguous + * IPv4 ranges collapse into ONE row spanning the full range; + * - a row with a DIFFERENT label (another city in the same state) stays separate; * - a same-label row separated by a GAP stays separate; * - IPv6 upstream rows are written to the sibling *.ipv6.csv, not the IPv4 file; * adjacent same-label IPv6 ranges merge; IPv6 is keyed on /64 prefixes, so a * later different-label slice of an already-claimed /64 is dropped. * - DB-IP 'ZZ' (private/reserved) rows are dropped from both outputs. + * - the places file carries one centroid per country, subdivision, and city + * label, sorted by label, averaged on the sphere (Fiji straddles 180°). * * No framework; PASS/FAIL counter + non-zero exit on failure. * Run: node tests/server-ip-geo-merge.test.js @@ -36,20 +38,25 @@ function check(label, cond, detail) { const inPath = path.join(os.tmpdir(), `fsb-dbip-merge-in-${process.pid}.csv`); const outPath = path.join(os.tmpdir(), `fsb-dbip-merge-out-${process.pid}.csv`); const ipv6OutPath = outPath.slice(0, -4) + '.ipv6.csv'; +const placesOutPath = outPath.slice(0, -4) + '.places.csv'; // Upstream DB-IP IP-to-City Lite shape: ip_start,ip_end,continent,country,stateprov,city,lat,lon const upstream = [ - '1.0.0.0,1.0.0.255,EU,DE,Bavaria,Munich,48.1,11.5', // \ - '1.0.1.0,1.0.1.255,EU,DE,Bavaria,Nuremberg,49.4,11.0', // >- adjacent same label -> merge into one - '1.0.2.0,1.0.2.255,EU,DE,Bavaria,Augsburg,48.3,10.8', // / - '1.0.3.0,1.0.3.255,EU,FR,Île-de-France,Paris,48.8,2.3', // different label -> separate - '2001:db8::,2001:db8::1,AS,JP,Tokyo,Tokyo,35.6,139.6', // \ - '2001:db8::2,2001:db8::3,AS,JP,Tokyo,Kyoto,35.0,135.7', // >- adjacent same IPv6 label -> merge - '2001:db8::4,2001:db8::5,AS,KR,Seoul,Seoul,37.5,127.0', // same /64, different label -> dropped (first label wins) - '2001:db8:1::,2001:db8:1::1,AS,JP,Osaka,Osaka,34.6,135.5', // different IPv6 subdivision - '10.0.0.0,10.255.255.255,ZZ,ZZ,,,0,0', // DB-IP private/reserved -> dropped + '1.0.0.0,1.0.0.255,EU,DE,Bavaria,Munich,48.1,11.5', // \ + '1.0.1.0,1.0.1.255,EU,DE,Bavaria,Munich,48.2,11.6', // >- adjacent same place -> merge into one + '1.0.2.0,1.0.2.255,EU,DE,Bavaria,Munich,48.1,11.6', // / + '1.0.3.0,1.0.3.255,EU,DE,Bavaria,Nuremberg,49.4,11.0', // adjacent, different city -> separate + '1.0.4.0,1.0.4.255,EU,FR,Île-de-France,Paris,48.8,2.3', // different country -> separate + '2001:db8::,2001:db8::1,AS,JP,Tokyo,Tokyo,35.6,139.6', // \ + '2001:db8::2,2001:db8::3,AS,JP,Tokyo,Tokyo,35.7,139.7', // >- adjacent same IPv6 place -> merge + '2001:db8::4,2001:db8::5,AS,KR,Seoul,Seoul,37.5,127.0', // same /64, different label -> dropped (first label wins) + '2001:db8:1::,2001:db8:1::1,AS,JP,Osaka,Osaka,34.6,135.5', // different IPv6 subdivision + '10.0.0.0,10.255.255.255,ZZ,ZZ,,,0,0', // DB-IP private/reserved -> dropped 'fd00::,fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff,ZZ,ZZ,,,0,0', // IPv6 ULA reserved -> dropped - '2.0.0.0,2.0.0.255,EU,DE,Bavaria,Regensburg,49.0,12.1', // same label but GAP -> separate + '2.0.0.0,2.0.0.255,EU,DE,Bavaria,Munich,48.1,11.5', // same place but GAP -> separate + '3.0.0.0,3.0.0.255,NA,US,California,"San Jose, Downtown",37.3,-121.9', // quoted comma in a city + '4.0.0.0,4.0.0.255,OC,FJ,Western,Lautoka,-17.6,177.4', // \ + '4.0.1.0,4.0.1.255,OC,FJ,Eastern,Lakeba,-18.2,-178.8', // >- Fiji spans the antimeridian ]; console.log('--- server-ip-geo-merge (260630-hct) ---'); @@ -64,15 +71,21 @@ try { const dataLines = text.split(/\r?\n/).filter((l) => l && !l.startsWith('#')); // 1.0.0.0=16777216, 1.0.2.255=16777983, 1.0.3.0=16777984, 1.0.3.255=16778239, - // 2.0.0.0=33554432, 2.0.0.255=33554687. - const MERGED_DE = '16777216,16777983,DE,Bavaria'; // three adjacent rows collapsed - const FR_ROW = '16777984,16778239,FR,Île-de-France'; // different label stays separate - const GAP_DE = '33554432,33554687,DE,Bavaria'; // same label, gap -> separate + // 1.0.4.0=16778240, 1.0.4.255=16778495, 2.0.0.0=33554432, 2.0.0.255=33554687, + // 3.0.0.0=50331648, 3.0.0.255=50331903. + const MERGED_MUNICH = '16777216,16777983,DE,Bavaria,Munich'; // three adjacent rows collapsed + const NUREMBERG = '16777984,16778239,DE,Bavaria,Nuremberg'; // other city, same state: separate + const FR_ROW = '16778240,16778495,FR,Île-de-France,Paris'; // different label stays separate + const GAP_MUNICH = '33554432,33554687,DE,Bavaria,Munich'; // same label, gap -> separate + const SAN_JOSE = '50331648,50331903,US,California,San Jose Downtown'; // comma stripped, not a column - check('exactly 3 merged data rows', dataLines.length === 3, `got ${dataLines.length}: ${JSON.stringify(dataLines)}`); - check('adjacent same-label DE/Bavaria collapsed to one span', dataLines.includes(MERGED_DE), `got ${JSON.stringify(dataLines)}`); - check('different label (FR/Île-de-France) stayed separate', dataLines.includes(FR_ROW), `got ${JSON.stringify(dataLines)}`); - check('gapped same-label DE/Bavaria stayed separate', dataLines.includes(GAP_DE), `got ${JSON.stringify(dataLines)}`); + check('exactly 7 merged data rows', dataLines.length === 7, `got ${dataLines.length}: ${JSON.stringify(dataLines)}`); + check('adjacent same-place DE/Bavaria/Munich collapsed to one span', dataLines.includes(MERGED_MUNICH), `got ${JSON.stringify(dataLines)}`); + check('adjacent other city (Nuremberg) stayed separate', dataLines.includes(NUREMBERG), `got ${JSON.stringify(dataLines)}`); + check('different label (FR/Île-de-France/Paris) stayed separate', dataLines.includes(FR_ROW), `got ${JSON.stringify(dataLines)}`); + check('gapped same-place Munich stayed separate', dataLines.includes(GAP_MUNICH), `got ${JSON.stringify(dataLines)}`); + check('a comma inside a quoted city never becomes a column', dataLines.includes(SAN_JOSE), `got ${JSON.stringify(dataLines)}`); + check('every row has exactly five columns', dataLines.every((l) => l.split(',').length === 5), `got ${JSON.stringify(dataLines)}`); check('IPv6 upstream row not in IPv4 file', !text.includes('Tokyo') && !/,JP,/.test(text), 'IPv6 row leaked into IPv4 output'); check('output is sorted ascending by start', (() => { const starts = dataLines.map((l) => Number(l.split(',')[0])); @@ -82,17 +95,40 @@ try { const v6text = fs.readFileSync(ipv6OutPath, 'utf8'); const v6lines = v6text.split(/\r?\n/).filter((l) => l && !l.startsWith('#')); - const MERGED_JP = '20010db800000000,20010db800000000,JP,Tokyo'; - const OSAKA = '20010db800010000,20010db800010000,JP,Osaka'; + const MERGED_JP = '20010db800000000,20010db800000000,JP,Tokyo,Tokyo'; + const OSAKA = '20010db800010000,20010db800010000,JP,Osaka,Osaka'; check('exactly 2 merged IPv6 data rows', v6lines.length === 2, `got ${v6lines.length}: ${JSON.stringify(v6lines)}`); check('adjacent same-label JP/Tokyo IPv6 collapsed', v6lines.includes(MERGED_JP), `got ${JSON.stringify(v6lines)}`); check('different IPv6 subdivision JP/Osaka stayed separate', v6lines.includes(OSAKA), `got ${JSON.stringify(v6lines)}`); check('later different-label slice of a claimed /64 dropped', !v6text.includes('Seoul'), `got ${JSON.stringify(v6lines)}`); check('DB-IP ZZ rows dropped from both outputs', !/,ZZ,/.test(text) && !/,ZZ,/.test(v6text), 'ZZ row written'); + + const placeLines = fs.readFileSync(placesOutPath, 'utf8').split(/\r?\n/).filter((l) => l && !l.startsWith('#')); + const places = new Map(placeLines.map((l) => { + const [label, lat, lon] = l.split(','); + return [label, { lat: Number(lat), lon: Number(lon) }]; + })); + const labels = placeLines.map((l) => l.split(',')[0]); + const near = (p, lat, lon) => !!p && Math.abs(p.lat - lat) <= 0.15 && Math.abs(p.lon - lon) <= 0.15; + check('places sorted by label in JS string order', + labels.every((l, i) => i === 0 || labels[i - 1] < l), `got ${JSON.stringify(labels)}`); + check('places has every level for Munich', + ['DE', 'DE-Bavaria', 'DE-Bavaria/Munich', 'DE-Bavaria/Nuremberg'].every((l) => places.has(l)), `got ${JSON.stringify(labels)}`); + check('US labels use the state code and the cleaned city', + places.has('US-CA') && places.has('US-CA/San Jose Downtown'), `got ${JSON.stringify(labels)}`); + check('Munich centroid is the mean of its four ranges', near(places.get('DE-Bavaria/Munich'), 48.1, 11.55), JSON.stringify(places.get('DE-Bavaria/Munich'))); + check('IPv6 rows feed the places file too', near(places.get('JP-Tokyo/Tokyo'), 35.65, 139.65), JSON.stringify(places.get('JP-Tokyo/Tokyo'))); + const fiji = places.get('FJ'); + check('Fiji averages across 180°, not to the prime-meridian side', + !!fiji && Math.abs(fiji.lon) > 170 && fiji.lat < -17 && fiji.lat > -18.5, JSON.stringify(fiji)); + check('no ZZ or unknown place', !places.has('ZZ') && !places.has('unknown') && !labels.some((l) => l.startsWith('ZZ')), `got ${JSON.stringify(labels)}`); + check('coordinates are rounded to 0.1 degree', + placeLines.every((l) => l.split(',').slice(1).every((n) => /^-?\d+(\.\d)?$/.test(n))), `got ${JSON.stringify(placeLines)}`); } finally { try { fs.unlinkSync(inPath); } catch { /* best effort */ } try { fs.unlinkSync(outPath); } catch { /* best effort */ } try { fs.unlinkSync(ipv6OutPath); } catch { /* best effort */ } + try { fs.unlinkSync(placesOutPath); } catch { /* best effort */ } } console.log(`\n=== server-ip-geo-merge results: ${passed} passed, ${failed} failed ===`); diff --git a/tests/server-ip-geo-scale.test.js b/tests/server-ip-geo-scale.test.js index 27959c1f..d7b91813 100644 --- a/tests/server-ip-geo-scale.test.js +++ b/tests/server-ip-geo-scale.test.js @@ -1,18 +1,17 @@ /** * Quick task 260630-hct (worldwide) -- ip-geo.js scale / memory-safety check. * - * The worldwide DB-IP dataset is hundreds of thousands to a few million ranges. - * This test builds a large synthetic compact dataset (default 1,000,000 ranges), - * points DBIP_DATASET_PATH at it, and verifies the struct-of-arrays loader - * (a) parses it without OOM/throw, (b) answers binary-search lookups correctly at - * scale, incl. a multibyte subdivision, and (c) returns 'unknown' for gap / - * out-of-range IPs. + * The worldwide city-level DB-IP dataset is a few million ranges. This test + * builds a large synthetic compact dataset (default 1,000,000 ranges), points + * DBIP_DATASET_PATH at it, and verifies the on-disk search (a) answers lookups + * correctly at scale, incl. a multibyte subdivision and city, and (b) returns + * 'unknown' for gap / out-of-range IPs. * * Memory: the dataset file is generated by STREAMING (no large in-process array), - * and the loader's footprint is measured in an ISOLATED child process that does - * nothing but load + look up -- so the reported RSS reflects the real server - * scenario, not this test's generation overhead. RSS is printed for observability - * and softly asserted under a generous ceiling well below the 256 MB VM limit. + * and the lookup footprint is measured in an ISOLATED child process that does + * nothing but open + look up -- so the reported RSS reflects the real server + * scenario, not this test's generation overhead. The file is searched in place, + * so RSS should stay near a bare Node process whatever the dataset size. * * No framework; PASS/FAIL counter + non-zero exit on failure. * Run: node tests/server-ip-geo-scale.test.js @@ -41,8 +40,9 @@ function intToIp(n) { // Distinct labels cycled across the synthetic ranges; index 5 is multibyte. const LABELS = [ - ['US', 'California'], ['US', 'Texas'], ['GB', 'England'], ['DE', 'Bavaria'], - ['IN', 'Maharashtra'], ['BR', 'São Paulo'], ['JP', 'Tokyo'], + ['US', 'California', 'San Jose'], ['US', 'Texas', 'Austin'], ['GB', 'England', 'London'], + ['DE', 'Bavaria', 'Munich'], ['IN', 'Maharashtra', 'Pune'], ['BR', 'São Paulo', 'São Paulo'], + ['JP', 'Tokyo', 'Tokyo'], ]; const N = 1000000; // number of synthetic ranges (~realistic worldwide upper end) const STRIDE = 512; // 256-wide range followed by a 256-wide gap (exercises misses) @@ -60,7 +60,7 @@ try { for (let i = 0; i < N; i++) { const start = i * STRIDE; const lab = LABELS[i % LABELS.length]; - buf += `${start},${start + 255},${lab[0]},${lab[1]}\n`; + buf += `${start},${start + 255},${lab[0]},${lab[1]},${lab[2]}\n`; if (buf.length >= (1 << 20)) { fs.writeSync(fd, buf); buf = ''; } } if (buf) fs.writeSync(fd, buf); @@ -74,8 +74,8 @@ try { const lab = LABELS[i % LABELS.length]; const ip = intToIp(start + offset); const r = ipGeo.deriveRegion(ip); - check(`range ${i} (${ip}) -> ${lab[0]}/${lab[1]}`, - r && typeof r === 'object' && r.country === lab[0] && r.subdivision === lab[1], + check(`range ${i} (${ip}) -> ${lab.join('/')}`, + r && typeof r === 'object' && r.country === lab[0] && r.subdivision === lab[1] && r.city === lab[2], `got ${JSON.stringify(r)}`); } @@ -95,14 +95,16 @@ try { check(`above-all address ${aboveIp} -> unknown`, ipGeo.deriveRegion(aboveIp) === 'unknown', `got ${JSON.stringify(ipGeo.deriveRegion(aboveIp))}`); - // Isolated memory probe: a fresh process that ONLY loads the dataset + does one - // lookup, then prints its peak RSS. This excludes this test's generation cost, - // so the number reflects the actual server load path. + // Isolated memory probe: a fresh process that ONLY opens the dataset and does + // a batch of lookups, then prints its RSS. This excludes this test's + // generation cost, so the number reflects the actual server lookup path. const probe = ` process.env.DBIP_DATASET_PATH = ${JSON.stringify(tmpPath)}; const g = require(${JSON.stringify(IP_GEO)}); - const r = g.deriveRegion('8.8.8.8'); // triggers the lazy load - if (typeof r === 'undefined') process.exit(3); + for (let i = 0; i < 2000; i++) { + const r = g.deriveRegion(\`\${i % 31}.\${(i * 7) % 256}.\${(i * 13) % 256}.\${i % 256}\`); + if (typeof r === 'undefined') process.exit(3); + } console.log(JSON.stringify({ rss: process.memoryUsage().rss })); `; let probeRssMb = null; @@ -113,11 +115,12 @@ try { probeRssMb = null; } if (probeRssMb !== null) { - console.log(` [info] isolated loader RSS for ${N} ranges: ${probeRssMb.toFixed(1)}MB`); - // Generous ceiling: a clean load of ~1M ranges should sit well under this and - // far below the 256 MB VM. Soft guard against a future representation regression. - check(`isolated loader RSS < 200MB (got ${probeRssMb.toFixed(1)}MB)`, probeRssMb < 200, - `RSS ${probeRssMb.toFixed(1)}MB exceeds 200MB ceiling`); + console.log(` [info] isolated lookup RSS for ${N} ranges: ${probeRssMb.toFixed(1)}MB`); + // A bare Node process is ~40-50 MB. Parsing 1M ranges into typed arrays + // would add ~12 MB+ per million rows; this guards against a regression back + // to loading the dataset into memory. + check(`isolated lookup RSS < 100MB (got ${probeRssMb.toFixed(1)}MB)`, probeRssMb < 100, + `RSS ${probeRssMb.toFixed(1)}MB exceeds 100MB ceiling`); } else { console.log(' [info] isolated RSS probe unavailable (skipped, non-fatal)'); } diff --git a/tests/server-ip-geo.test.js b/tests/server-ip-geo.test.js index 5711bdb6..6135754d 100644 --- a/tests/server-ip-geo.test.js +++ b/tests/server-ip-geo.test.js @@ -10,6 +10,8 @@ * (c) malformed / empty / unmapped IPv6 (loopback, Google DNS, CIDR keys) * returns 'unknown'. Native IPv6 in the sibling fixture (2405:201::/32, * 2001:db8::/32) hits. IPv4-mapped IPv6 (`::ffff:a.b.c.d`) unwraps as IPv4. + * (d) city: five-column rows carry a city, four-column rows parse with an + * empty one, and placeCentroid() resolves labels from the places file. * * Each scenario re-requires the module via _resetForTest() so the lazy table * cache is rebuilt against the scenario's DBIP_DATASET_PATH. @@ -33,8 +35,9 @@ function check(label, cond, detail) { } function isUnknown(v) { return v === 'unknown'; } -function regionEq(v, country, subdivision) { - return v && typeof v === 'object' && v.country === country && v.subdivision === subdivision; +function regionEq(v, country, subdivision, city) { + return !!v && typeof v === 'object' && v.country === country && v.subdivision === subdivision + && (city === undefined || v.city === city); } console.log('--- server-ip-geo (260630-hct) ---'); @@ -62,17 +65,24 @@ r = ipGeo.deriveRegion('203.0.113.5'); check('203.0.113.5 -> AU/Victoria (fixture hit, high non-US range)', regionEq(r, 'AU', 'Victoria'), `got ${JSON.stringify(r)}`); // Worldwide (non-US) subdivisions: these are preserved with full granularity by -// the loader (and later rendered as e.g. 'GB-England' by regionLabel). The +// the lookup (and later rendered as e.g. 'GB-England' by regionLabel). The // dataset is worldwide; assert several continents resolve to the right -// {country, subdivision} via the typed-array struct-of-arrays path. +// {country, subdivision} through the on-disk search. check('5.5.5.50 -> GB/England (worldwide fixture hit)', regionEq(ipGeo.deriveRegion('5.5.5.50'), 'GB', 'England'), `got ${JSON.stringify(ipGeo.deriveRegion('5.5.5.50'))}`); check('10.10.10.10 -> DE/Bavaria (worldwide fixture hit)', regionEq(ipGeo.deriveRegion('10.10.10.10'), 'DE', 'Bavaria'), `got ${JSON.stringify(ipGeo.deriveRegion('10.10.10.10'))}`); check('20.20.20.200 -> IN/Maharashtra (worldwide fixture hit)', regionEq(ipGeo.deriveRegion('20.20.20.200'), 'IN', 'Maharashtra'), `got ${JSON.stringify(ipGeo.deriveRegion('20.20.20.200'))}`); // BR/São Paulo: the accented subdivision must round-trip byte-for-byte through -// readFileSync('utf8') -> typed-array line parse -> interned {country,subdivision}. +// the byte-level line search and its utf8 decode. r = ipGeo.deriveRegion('30.30.30.30'); check('30.30.30.30 -> BR/São Paulo (multibyte subdivision round-trips)', regionEq(r, 'BR', 'São Paulo'), `got ${JSON.stringify(r)}`); +// City column: five-column rows carry it; the four-column Texas row keeps the +// older state-level shape and parses with an empty city; SG has no subdivision. +check('8.8.8.8 -> city Mountain View', regionEq(ipGeo.deriveRegion('8.8.8.8'), 'US', 'California', 'Mountain View'), JSON.stringify(ipGeo.deriveRegion('8.8.8.8'))); +check('30.30.30.30 -> multibyte city São Paulo', regionEq(ipGeo.deriveRegion('30.30.30.30'), 'BR', 'São Paulo', 'São Paulo'), JSON.stringify(ipGeo.deriveRegion('30.30.30.30'))); +check('1.1.1.42 -> four-column row parses with city \'\'', regionEq(ipGeo.deriveRegion('1.1.1.42'), 'US', 'Texas', ''), JSON.stringify(ipGeo.deriveRegion('1.1.1.42'))); +check('40.40.40.40 -> SG with no subdivision, city Singapore', regionEq(ipGeo.deriveRegion('40.40.40.40'), 'SG', '', 'Singapore'), JSON.stringify(ipGeo.deriveRegion('40.40.40.40'))); + // Exact range boundaries are inclusive. check('8.8.8.0 (range start, inclusive)', regionEq(ipGeo.deriveRegion('8.8.8.0'), 'US', 'California'), 'start boundary missed'); check('8.8.8.255 (range end, inclusive)', regionEq(ipGeo.deriveRegion('8.8.8.255'), 'US', 'California'), 'end boundary missed'); @@ -150,6 +160,11 @@ check( regionEq(ipGeo.deriveRegion('2405:201:e00:1::1'), 'IN', 'Maharashtra'), `got ${JSON.stringify(ipGeo.deriveRegion('2405:201:e00:1::1'))}` ); +check( + 'native IPv6 carries its city too', + regionEq(ipGeo.deriveRegion('2405:201:e00:1::1'), 'IN', 'Maharashtra', 'Mumbai'), + `got ${JSON.stringify(ipGeo.deriveRegion('2405:201:e00:1::1'))}` +); check( 'native IPv6 2405:201:: range start -> IN/Maharashtra', regionEq(ipGeo.deriveRegion('2405:201::'), 'IN', 'Maharashtra'), @@ -221,6 +236,76 @@ check('::1 -> unknown (loopback not in fixture)', isUnknown(ipGeo.deriveRegion(' fs.rmSync(dir, { recursive: true, force: true }); } +// ============================================================================= +// (f) Place centroids from the sibling places fixture. +// ============================================================================= +process.env.DBIP_DATASET_PATH = FIXTURE; +delete process.env.DBIP_PLACES_DATASET_PATH; +ipGeo._resetForTest(); +{ + const near = (v, lat, lon) => !!v && v.lat === lat && v.lon === lon; + check('siblingPlacesPath maps the fixture to *.places.fixture.csv', + ipGeo.siblingPlacesPath(FIXTURE).endsWith('dbip-city-lite.places.fixture.csv'), ipGeo.siblingPlacesPath(FIXTURE)); + check('placeCentroid city label', near(ipGeo.placeCentroid('US-CA/Mountain View'), 37.4, -122.1), JSON.stringify(ipGeo.placeCentroid('US-CA/Mountain View'))); + check('placeCentroid subdivision label', near(ipGeo.placeCentroid('US-CA'), 36.7, -119.6), JSON.stringify(ipGeo.placeCentroid('US-CA'))); + check('placeCentroid country label', near(ipGeo.placeCentroid('US'), 38.6, -93.2), JSON.stringify(ipGeo.placeCentroid('US'))); + check('placeCentroid first label in the file', near(ipGeo.placeCentroid('AU'), -25.3, 134), JSON.stringify(ipGeo.placeCentroid('AU'))); + check('placeCentroid last label in the file', near(ipGeo.placeCentroid('US-TX'), 31.2, -97.8), JSON.stringify(ipGeo.placeCentroid('US-TX'))); + check('placeCentroid multibyte label', near(ipGeo.placeCentroid('BR-São-Paulo/São Paulo'), -23.6, -46.6), JSON.stringify(ipGeo.placeCentroid('BR-São-Paulo/São Paulo'))); + check('placeCentroid city without subdivision', near(ipGeo.placeCentroid('SG/Singapore'), 1.3, 103.9), JSON.stringify(ipGeo.placeCentroid('SG/Singapore'))); + for (const miss of ['US-CA/Fresno', 'ZZ', 'A', 'zzz', 'Other', 'unknown', '', null, 42]) { + check(`placeCentroid(${JSON.stringify(miss)}) -> null`, ipGeo.placeCentroid(miss) === null, JSON.stringify(ipGeo.placeCentroid(miss))); + } + + process.env.DBIP_PLACES_DATASET_PATH = path.join(__dirname, '__no_such_dir__', 'places.csv'); + ipGeo._resetForTest(); + let threw = false; + let absent; + try { absent = ipGeo.placeCentroid('US-CA'); } catch { threw = true; } + check('absent places file: placeCentroid -> null, no throw', !threw && absent === null, `threw=${threw} got ${JSON.stringify(absent)}`); + check('absent places file does not affect deriveRegion', regionEq(ipGeo.deriveRegion('8.8.8.8'), 'US', 'California', 'Mountain View'), JSON.stringify(ipGeo.deriveRegion('8.8.8.8'))); + delete process.env.DBIP_PLACES_DATASET_PATH; +} + +// ============================================================================= +// (g) On-disk search edge cases: CRLF line endings, no trailing newline, a +// long header, and a bogus line in the middle of the data. +// ============================================================================= +{ + const fs = require('fs'); + const os = require('os'); + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'fsb-ip-geo-disk-')); + const v4 = path.join(dir, 'geo.csv'); + const header = Array.from({ length: 400 }, (_, i) => `# header line ${i} ${'x'.repeat(40)}`); + const rows = []; + for (let i = 0; i < 5000; i++) { + const start = i * 1024; + rows.push(`${start},${start + 511},US,California,City ${i}`); + if (i === 2500) rows.push('this line is not data'); + } + fs.writeFileSync(v4, header.concat(rows).join('\r\n')); + process.env.DBIP_DATASET_PATH = v4; + process.env.DBIP_IPV6_DATASET_PATH = path.join(dir, 'none.ipv6.csv'); + ipGeo._resetForTest(); + const ipOf = (n) => [(n >>> 24) & 255, (n >>> 16) & 255, (n >>> 8) & 255, n & 255].join('.'); + let allHit = true; + let firstMiss = null; + for (const i of [0, 1, 2499, 2500, 2501, 4998, 4999]) { + for (const off of [0, 255, 511]) { + const r = ipGeo.deriveRegion(ipOf(i * 1024 + off)); + if (!regionEq(r, 'US', 'California', `City ${i}`)) { allHit = false; firstMiss = firstMiss || { i, off, r }; } + } + } + check('CRLF rows, long header, mid-file junk: every probe hits its own city', allHit, JSON.stringify(firstMiss)); + check('gap between CRLF rows -> unknown', isUnknown(ipGeo.deriveRegion(ipOf(2500 * 1024 + 700))), JSON.stringify(ipGeo.deriveRegion(ipOf(2500 * 1024 + 700)))); + check('past the last row (no trailing newline) -> unknown', isUnknown(ipGeo.deriveRegion(ipOf(5000 * 1024))), 'expected unknown'); + + delete process.env.DBIP_IPV6_DATASET_PATH; + process.env.DBIP_DATASET_PATH = FIXTURE; + ipGeo._resetForTest(); + fs.rmSync(dir, { recursive: true, force: true }); +} + console.log(`\n=== server-ip-geo results: ${passed} passed, ${failed} failed ===`); if (failed > 0) process.exit(1); process.exit(0); diff --git a/tests/server-region-aggregation.test.js b/tests/server-region-aggregation.test.js index d44525b1..ab07bd7d 100644 --- a/tests/server-region-aggregation.test.js +++ b/tests/server-region-aggregation.test.js @@ -7,15 +7,17 @@ * housekeeper tick, reads telemetry_global_aggregates.popular_region_json, and asserts: * * (a) a region with >=5 distinct installs surfaces with its real uniq; - * (b) regions with <5 distinct installs collapse into a single 'Other' bucket - * equal to the SUM of their installs; - * (c) when the total below-k sum is itself <5 the 'Other' bucket is SUPPRESSED; + * (b) a label under the floor rolls up to its parent (city -> subdivision -> + * country); countries still under it collapse into a single 'Other' + * bucket equal to the SUM of their installs; + * (c) when that remainder is itself <5 the 'Other' bucket is SUPPRESSED; * (d) events whose region is 'unknown' are handled by the same floor; * and the public headline path (buildHeadlineJson) exposes popular_regions as * {label, uniq} with NO sub-floor/UUID/ip_hash leak. users_by_region_365d is * last successful geo (a newer 'unknown' day, or a later unknown event on the * same day, does not replace a real region) and is bounded to the same - * today..today-364 window as users_365d. + * today..today-364 window as users_365d. Published places carry the places + * file's centroid; 'Other' never does. * * Run: node tests/server-region-aggregation.test.js */ @@ -24,12 +26,18 @@ const path = require('path'); +// The public headline looks up place centroids; point it at the fixtures. +process.env.DBIP_DATASET_PATH = path.join(__dirname, '..', 'showcase', 'server', 'data', 'dbip-city-lite.fixture.csv'); +delete process.env.DBIP_IPV6_DATASET_PATH; +delete process.env.DBIP_PLACES_DATASET_PATH; + const SERVER_NM = path.join(__dirname, '..', 'showcase', 'server', 'node_modules'); const Database = require(require.resolve('better-sqlite3', { paths: [SERVER_NM] })); const { initializeDatabase } = require(path.join(__dirname, '..', 'showcase', 'server', 'src', 'db', 'schema')); const Queries = require(path.join(__dirname, '..', 'showcase', 'server', 'src', 'db', 'queries')); -const { runHousekeeperTick, floorToUtcDayMs, REGION_K_FLOOR } = require(path.join(__dirname, '..', 'showcase', 'server', 'src', 'telemetry', 'housekeeper')); +const { runHousekeeperTick, floorToUtcDayMs, applyRegionKFloor, REGION_K_FLOOR } = require(path.join(__dirname, '..', 'showcase', 'server', 'src', 'telemetry', 'housekeeper')); +const { regionLabel, regionParent, regionDepth } = require(path.join(__dirname, '..', 'showcase', 'server', 'src', 'utils', 'region-label')); const { buildHeadlineJson } = require(path.join(__dirname, '..', 'showcase', 'server', 'src', 'routes', 'public-stats')); let passed = 0; @@ -78,12 +86,13 @@ console.log('--- server-region-aggregation (260630-hct) ---'); check('REGION_K_FLOOR is the HARD k>=5 (not the relaxed mcp floor of 2)', REGION_K_FLOOR === 5, `got ${REGION_K_FLOOR}`); // ============================================================================= -// Scenario 1: above-floor region surfaces; below-floor regions collapse to a -// single 'Other' bucket equal to the SUM; 'unknown' folds in under the floor. -// US-CA: 6 distinct installs (>=5 -> surfaces, uniq=6) -// US-NY: 3 distinct installs (<5) -// US-TX: 2 distinct installs (<5) -// unknown: 1 distinct install (<5) +// Scenario 1: above-floor region surfaces; below-floor regions that cannot +// clear the floor at country level collapse to a single 'Other' bucket equal to +// the SUM; 'unknown' folds in under the floor. +// US-CA: 6 distinct installs (>=5 -> surfaces, uniq=6) +// FR-Normandy: 3 distinct installs (<5 -> FR 3 < 5 -> Other) +// DE-Bavaria: 2 distinct installs (<5 -> DE 2 < 5 -> Other) +// unknown: 1 distinct install (<5 -> Other) // below-k sum = 3 + 2 + 1 = 6 >= 5 -> single 'Other' bucket uniq=6. // ============================================================================= { @@ -91,8 +100,8 @@ check('REGION_K_FLOOR is the HARD k>=5 (not the relaxed mcp floor of 2)', REGION initializeDatabase(db); const queries = new Queries(db); seedRegion(queries, 'US-CA', 6); - seedRegion(queries, 'US-NY', 3); - seedRegion(queries, 'US-TX', 2); + seedRegion(queries, 'FR-Normandy', 3); + seedRegion(queries, 'DE-Bavaria', 2); seedRegion(queries, 'unknown', 1); runHousekeeperTick(db, queries, NOW); @@ -107,17 +116,35 @@ check('REGION_K_FLOOR is the HARD k>=5 (not the relaxed mcp floor of 2)', REGION check('S1: exactly ONE Other bucket', pr.filter((r) => r.region === 'Other').length === 1, `got ${JSON.stringify(pr)}`); - // No sub-floor region label leaks (US-NY / US-TX must NOT appear by name). - check('S1: below-floor US-NY does NOT leak by name', !pr.some((r) => r.region === 'US-NY'), `got ${JSON.stringify(pr)}`); - check('S1: below-floor US-TX does NOT leak by name', !pr.some((r) => r.region === 'US-TX'), `got ${JSON.stringify(pr)}`); - // 'unknown' was below the floor here, so it folded into Other (did not surface). - check('S1: sub-floor unknown folded into Other (not surfaced)', !pr.some((r) => r.region === 'unknown'), `got ${JSON.stringify(pr)}`); + // No sub-floor region label leaks, at any level. + for (const label of ['FR-Normandy', 'FR', 'DE-Bavaria', 'DE', 'unknown']) { + check(`S1: below-floor ${label} does NOT leak by name`, !pr.some((r) => r.region === label), `got ${JSON.stringify(pr)}`); + } // Every surfaced label is >= the floor. check('S1: every surfaced region.uniq >= REGION_K_FLOOR', pr.every((r) => r.uniq >= REGION_K_FLOOR), `got ${JSON.stringify(pr)}`); db.close(); } +// ============================================================================= +// Scenario 1b: sub-floor states of one country pool into that country. +// US-NY 3 + US-TX 2 -> 'US' 5 (published); neither state leaks by name. +// ============================================================================= +{ + const db = new Database(':memory:'); + initializeDatabase(db); + const queries = new Queries(db); + seedRegion(queries, 'US-NY', 3); + seedRegion(queries, 'US-TX', 2); + + runHousekeeperTick(db, queries, NOW); + const pr = readPopularRegion(db); + check('S1b: two sub-floor US states publish as US=5', + pr.length === 1 && pr[0].region === 'US' && pr[0].uniq === 5, `got ${JSON.stringify(pr)}`); + + db.close(); +} + // ============================================================================= // Scenario 2: total below-k sum is itself < 5 -> 'Other' bucket SUPPRESSED. // US-CA: 5 distinct installs (>=5 -> surfaces, uniq=5) @@ -195,7 +222,7 @@ check('REGION_K_FLOOR is the HARD k>=5 (not the relaxed mcp floor of 2)', REGION // ============================================================================= // Scenario 5: the same five installs first appear in an above-floor region and // later move into five separate sub-floor regions. They must be assigned once, -// using their latest daily region, rather than surfacing CA=5 plus Other=5. +// using their latest daily region, rather than surfacing CA=5 plus US=5. // ============================================================================= { const db = new Database(':memory:'); @@ -216,7 +243,7 @@ check('REGION_K_FLOOR is the HARD k>=5 (not the relaxed mcp floor of 2)', REGION runHousekeeperTick(db, queries, NOW + 2000); const pr = readPopularRegion(db); check('S5: roaming installs are counted once across the whole region breakdown', - Array.isArray(pr) && pr.length === 1 && pr[0].region === 'Other' && pr[0].uniq === 5, + Array.isArray(pr) && pr.length === 1 && pr[0].region === 'US' && pr[0].uniq === 5, `got ${JSON.stringify(pr)}`); check('S5: prior US-CA membership does not also surface', !pr.some((row) => row.region === 'US-CA'), @@ -225,6 +252,103 @@ check('REGION_K_FLOOR is the HARD k>=5 (not the relaxed mcp floor of 2)', REGION db.close(); } +// ============================================================================= +// Scenario 6: city -> subdivision -> country -> Other, one bucket per install. +// US-CA/San Jose 5 -> published as the city +// US-CA/Fresno 2 + US-CA/Oakland 1 + US-CA 2 -> 'US-CA' 5 +// US-TX/Austin 3 + US-NY 2 -> 'US' 5 +// SG/Singapore 2 + SG 1, JP-Tokyo/Tokyo 2 -> countries 3 and 2 +// -> 'Other' 5 +// ============================================================================= +{ + const db = new Database(':memory:'); + initializeDatabase(db); + const queries = new Queries(db); + const seeded = [ + ['US-CA/San Jose', 5], ['US-CA/Fresno', 2], ['US-CA/Oakland', 1], ['US-CA', 2], + ['US-TX/Austin', 3], ['US-NY', 2], ['SG/Singapore', 2], ['SG', 1], ['JP-Tokyo/Tokyo', 2], + ]; + for (const [region, n] of seeded) seedRegion(queries, region, n); + + runHousekeeperTick(db, queries, NOW); + const pr = readPopularRegion(db); + check('S6: city, state, country and Other each publish in size order', + JSON.stringify(pr) === JSON.stringify([ + { region: 'US', uniq: 5 }, + { region: 'US-CA', uniq: 5 }, + { region: 'US-CA/San Jose', uniq: 5 }, + { region: 'Other', uniq: 5 }, + ]), + `got ${JSON.stringify(pr)}`); + const seededTotal = seeded.reduce((sum, [, n]) => sum + n, 0); + check('S6: buckets are disjoint -- they sum to the installs seeded', + pr.reduce((sum, r) => sum + r.uniq, 0) === seededTotal, `got ${JSON.stringify(pr)} for ${seededTotal}`); + + db.close(); +} + +// ============================================================================= +// applyRegionKFloor directly: suppression at every level, first label wins. +// ============================================================================= +{ + const rows = (pairs) => pairs.flatMap(([region, n], i) => + Array.from({ length: n }, (_, j) => ({ region, install_uuid: `${i}-${j}` }))); + + check('floor: a lone sub-floor city rolls all the way up and is suppressed', + applyRegionKFloor(rows([['US-CA/Fresno', 4]]), 'install_uuid', 5).length === 0, 'expected []'); + check('floor: 4 installs in one city + 1 elsewhere in the state publish the state', + JSON.stringify(applyRegionKFloor(rows([['US-CA/Fresno', 4], ['US-CA/Oakland', 1]]), 'install_uuid', 5)) + === JSON.stringify([{ region: 'US-CA', uniq: 5 }]), + JSON.stringify(applyRegionKFloor(rows([['US-CA/Fresno', 4], ['US-CA/Oakland', 1]]), 'install_uuid', 5))); + check('floor: a city with no subdivision rolls straight to its country', + JSON.stringify(applyRegionKFloor(rows([['SG/Singapore', 3], ['SG', 2]]), 'install_uuid', 5)) + === JSON.stringify([{ region: 'SG', uniq: 5 }]), + JSON.stringify(applyRegionKFloor(rows([['SG/Singapore', 3], ['SG', 2]]), 'install_uuid', 5))); + const dup = [ + { region: 'US-CA/San Jose', install_uuid: 'a' }, + { region: 'US-NY', install_uuid: 'a' }, + ]; + check('floor: an install listed twice keeps its first label only', + JSON.stringify(applyRegionKFloor(dup, 'install_uuid', 1)) + === JSON.stringify([{ region: 'US-CA/San Jose', uniq: 1 }]), + JSON.stringify(applyRegionKFloor(dup, 'install_uuid', 1))); + check('floor: malformed rows are ignored', + applyRegionKFloor([null, {}, { region: '', install_uuid: 'x' }, { region: 'US', install_uuid: 7 }], 'install_uuid', 1).length === 0, + 'expected []'); +} + +// ============================================================================= +// regionLabel / regionParent / regionDepth. +// ============================================================================= +{ + const cases = [ + [{ country: 'US', subdivision: 'California', city: 'San Jose' }, 'US-CA/San Jose'], + [{ country: 'US', subdivision: 'California' }, 'US-CA'], + [{ country: 'us', subdivision: 'California', city: '' }, 'US-CA'], + [{ country: 'AU', subdivision: 'New South Wales', city: 'Sydney' }, 'AU-New-South-Wales/Sydney'], + [{ country: 'SG', subdivision: '', city: 'Singapore' }, 'SG/Singapore'], + [{ country: 'DE' }, 'DE'], + [{ country: 'US', subdivision: 'Puerto Rico' }, 'US-Puerto-Rico'], + [{ country: 'XX', subdivision: 'A/B', city: 'C/D E\tF' }, 'XX-A-B/C D E F'], + [{ country: 'IN', subdivision: 'Maharashtra', city: 'x'.repeat(60) }, `IN-Maharashtra/${'x'.repeat(40)}`], + [{ country: '' }, 'unknown'], + ['unknown', 'unknown'], + [null, 'unknown'], + ]; + for (const [input, want] of cases) { + check(`regionLabel(${JSON.stringify(input)}) -> ${want}`, regionLabel(input) === want, `got ${regionLabel(input)}`); + } + const parents = [ + ['US-CA/Winston-Salem', 'US-CA', 2], ['US-CA', 'US', 1], ['SG/Singapore', 'SG', 1], + ['AU-New-South-Wales', 'AU', 1], ['US', null, 0], ['unknown', null, 0], ['Other', null, 0], + ]; + for (const [label, parent, depth] of parents) { + check(`regionParent(${label}) -> ${parent}, depth ${depth}`, + regionParent(label) === parent && regionDepth(label) === depth, + `got ${regionParent(label)} / ${regionDepth(label)}`); + } +} + // ============================================================================= // Public headline: buildHeadlineJson exposes popular_regions as {label, uniq} // with no sub-floor leak and no UUID/ip_hash fields. @@ -235,16 +359,21 @@ check('REGION_K_FLOOR is the HARD k>=5 (not the relaxed mcp floor of 2)', REGION const queries = new Queries(db); seedRegion(queries, 'US-CA', 6); seedRegion(queries, 'US-NY', 5); - seedRegion(queries, 'US-TX', 2); // below floor -> folds into Other (sum 2 < 5 -> suppressed) + seedRegion(queries, 'US-TX', 2); // below floor -> US 2 -> Other 2 -> suppressed runHousekeeperTick(db, queries, NOW); const headline = buildHeadlineJson(queries, NOW); check('HL: headline has popular_regions field', 'popular_regions' in headline, `keys=${Object.keys(headline)}`); - check('HL: popular_regions is an array of {label, uniq}', + check('HL: popular_regions is an array of {label, uniq, lat?, lon?}', Array.isArray(headline.popular_regions) && headline.popular_regions.every( - (x) => typeof x.label === 'string' && Number.isInteger(x.uniq)), + (x) => typeof x.label === 'string' && Number.isInteger(x.uniq) + && Object.keys(x).every((k) => ['label', 'uniq', 'lat', 'lon'].includes(k))), `got ${JSON.stringify(headline.popular_regions)}`); + check('HL: published places carry the fixture centroid', + headline.popular_regions.some((x) => x.label === 'US-CA' && x.lat === 36.7 && x.lon === -119.6) + && headline.users_by_region_365d.some((x) => x.label === 'US-NY' && x.lat === 42.2 && x.lon === -74.9), + `got ${JSON.stringify(headline.popular_regions)} / ${JSON.stringify(headline.users_by_region_365d)}`); check('HL: popular_regions includes US-CA with uniq=6', headline.popular_regions.some((x) => x.label === 'US-CA' && x.uniq === 6), `got ${JSON.stringify(headline.popular_regions)}`); @@ -537,6 +666,30 @@ check('REGION_K_FLOOR is the HARD k>=5 (not the relaxed mcp floor of 2)', REGION db.close(); } +// ============================================================================= +// Public headline at city level: a city that clears the floor carries its +// centroid; 'Other' carries none. +// ============================================================================= +{ + const db = new Database(':memory:'); + initializeDatabase(db); + const queries = new Queries(db); + seedRegion(queries, 'US-CA/Mountain View', 5); + seedRegion(queries, 'GB-England/London', 3); + seedRegion(queries, 'DE-Bavaria/Munich', 2); + runHousekeeperTick(db, queries, NOW); + const headline = buildHeadlineJson(queries, NOW); + const census = headline.users_by_region_365d; + check('HL-city: 365d census publishes the city with its centroid', + census.some((x) => x.label === 'US-CA/Mountain View' && x.uniq === 5 && x.lat === 37.4 && x.lon === -122.1), + `got ${JSON.stringify(census)}`); + const other = census.find((x) => x.label === 'Other'); + check('HL-city: Other (GB 3 + DE 2) has no coordinates', + !!other && other.uniq === 5 && !('lat' in other) && !('lon' in other), `got ${JSON.stringify(census)}`); + check('HL-city: no sub-floor city leaks', !census.some((x) => /London|Munich|GB|DE/.test(x.label)), `got ${JSON.stringify(census)}`); + db.close(); +} + console.log(`\n=== server-region-aggregation results: ${passed} passed, ${failed} failed ===`); if (failed > 0) process.exit(1); process.exit(0);