Data & self-hosting
Effective date: 2026-09-12
Overview
When teams use PinSay to report visual defects or request UI changes, PinSay collects only the technical element and layout context required for developers and AI agents to locate and modify code. This document details the exact data schema captured, privacy controls, storage boundaries, and self-hosted deployments.
What is never captured
PinSay is engineered specifically for UI annotations and element inspection. The following data categories are never captured by the widget or extension:
- Application cookies & session tokens — authentication cookies, session headers, and auth tokens remain untouched.
- Browser storage — your application's
localStorage,sessionStorage, and IndexedDB data are never accessed. - Request & response payloads by default — HTTP request bodies and response contents are captured only when a project's admin turns on "Request and response content" for bug reports (see Opt-in extras), and then with secrets removed; cookies, authorization and other secret headers are never captured.
- Keystrokes & background tracking — there is no session replay, keystroke logging or form value scraping. When bug capture is on, the widget keeps a short local list of steps (what was clicked, which field was typed in and how many characters, never the text) that leaves the browser only with a comment marked as a bug.
- Pre-click interactions & unrelated pages — nothing is transmitted before an authenticated stakeholder enters comment mode and clicks an element. One exception worth stating plainly: when a project enables bug capture, the widget buffers recent network calls, console errors/warnings and steps locally in the browser from the moment it loads, so a bug report can include what happened just before it. On a site opened with the PinSay browser extension, the extension also notes uncaught errors from the moment the page starts loading, in the same local buffer. That buffer never leaves the browser unless the stakeholder marks it as a bug on a comment they submit.
Opt-in extras
Two optional capabilities exist on a strict opt-in basis:
- Element screenshots — Controlled via a checkbox on each comment. The screenshot is a high-fidelity reconstruction of what the stakeholder sees at comment time (rebuilt from the DOM, so cross-origin images and canvas/video content may differ). Can be completely disabled across the application by passing
screenshot="false"to the<pinsay-feedback>element. When the comment is made through the PinSay browser extension, the screenshot is taken from the real pixels of the visible tab instead (falling back to the reconstruction if that is not possible). - Bug reports — Available only when bug capture is on in the project's settings and the stakeholder marks the comment as a bug. Attaches the window before the comment (30 seconds by default): every fetch/XHR call with method, URL, status, duration, start time and only these headers:
accept,cache-control,content-length,content-type,etag,request-id,retry-after,traceparent,x-correlation-id,x-request-id(any header whose name contains authorization, cookie, token, secret, password, session, api-key, csrf or xsrf is never kept); console errors and warnings (plus uncaught errors and unhandled promise rejections, and pages or files the browser failed to load with a status of 400 or higher, with their address); and the user's steps (clicks with a short label, typing as a character count only, field changes, route changes). Query strings are removed. Request and response content is a separate project setting, off by default: when on, JSON and text bodies up to 4,000 characters each are kept and query strings are kept, with values whose names look secret (such as passwords, tokens, keys, cookies, authorization values, sessions, CSRF tokens, card numbers, CVV/CVC, IBAN, PIN) and anything that looks like a bearer token or JWT replaced by[REDACTED]; binary, file and streaming bodies are never read. URLs on the project's deny-list are recorded without headers, query values or content. The same limits are enforced again on the server. Reports are deleted at once with their comment, project or workspace, and 30 days after every comment that used them has been deleted (or a longer period set for the workspace).
Controls & deployment guards
Engineering teams have complete control over widget activation:
- Environment guards — Restrict the widget loader behind build-time or environment variables (e.g.
VITE_PINSAY_ENABLEDorNEXT_PUBLIC_PINSAY_ENABLED) so PinSay is never mounted in public customer environments. - Screenshot lockout — Host applications can disable screenshot capability entirely by adding
screenshot="false"to the<pinsay-feedback>element. - Targeted backend URLs — The widget, CLI, and extension are thin clients that communicate only with the explicit server endpoint configured by the host application.
Where it lives: Hosted vs. Self-hosted
PinSay is designed with a strict client-server boundary, allowing organizations to run PinSay either as a managed cloud service or self-hosted entirely within their own infrastructure:
- Hosted cloud — Managed API and PostgreSQL database hosted in Riyadh, Saudi Arabia (Oracle Cloud, me-riyadh-1). TLS encryption in transit and continuous updates.
- Self-hosted — Deploy PinSay's containerized API and PostgreSQL database inside your own cloud VPC (AWS, GCP, Azure) or on-premises servers. All comments, screenshots, and metadata remain strictly within your network perimeter.
| Component | Hosted Service | Self-Hosted Instance |
|---|---|---|
| Feedback Widget | Runs in client browser; connects to hosted API | Runs in client browser; connects to your internal API |
| API & Database | Managed cloud instance | Your own servers and PostgreSQL database |
| Screenshots & Blobs | Local filesystem storage (Docker volume) | Local filesystem storage (Docker volume) |
| Developer CLI & MCP | Runs locally on developer workstation | Runs locally on developer workstation |
Retention & deletion
Feedback comments and projects are retained until deleted by an author or workspace administrator.
Deleting a comment or project hides it everywhere immediately.
Under the hood, deleting a comment or a project is a soft delete: the comment and project records are stamped with DeletedAt and disappear from all active views, queries, APIs, and exports.
However, the underlying comment rows remain in storage. A screenshot file is deleted at once when its author removes the image via edit, and the screenshot files of deleted comments are purged 30 days after the comment is deleted. Physical purge of deleted comment rows is on our roadmap; until then, contact us for a manual purge.
Bug reports (network calls, console entries, steps) are deleted immediately with their comment, project or workspace. A report is deleted after 30 days, once every comment that used it has been deleted (or the longer period set for the workspace).
Plan-specific retention durations (RetentionDays) are available on request for custom enterprise configurations.
Security & workspace isolation
PinSay implements defense-in-depth security across all architectural layers:
- Credential protection — User passwords are encrypted with strong cryptographic hashing algorithms. API keys are stored hashed (SHA-256) for lookup and encrypted for display.
- Session management — Authentication relies on signed JSON Web Tokens (JWT) with a 12-hour lifetime and per-request security stamp validation.
- Database multi-tenancy — Strict database-level query filters enforce multi-tenant isolation, preventing cross-workspace data access or leakage.
- Safe AI automation — PinSay's own CLI and MCP server expose no
git pushcapability at all; a test asserts no command they run contains it. The instructions PinSay hands your AI tool also forbid pushing. That is a strong guard, not a sandbox: an AI agent with general shell access is restrained by those instructions rather than by PinSay. Code edits happen locally and a human pushes.
The PinSay CLI
The PinSay CLI (@pinsay/cli) connects your local code repository to PinSay. Its data collection and operational posture are strictly bounded:
- No source code read or uploaded: The CLI never reads or uploads your source code. Design tokens detected locally remain stored in
.pinsay/stack.jsonon your workstation and are never transmitted. - Always sent: Essential operations required for basic functionality: user authentication / sign-in, project lookup and project creation, comment operations executed by you or your agent (list, get, apply/mark, reply, status), agent skill downloads, and version verification.
- Sent only when you agree to share: During setup, the CLI asks whether to share framework names (e.g. react, vite, dotnet), the detected AI tool, and usage telemetry (
installed,doctor_run,apply_started,first_apply). The default answer is Yes (in interactive sessions and in non-interactive environments like CI, headless agents, or with--yes). - When you decline (No or
--no-share-stack): None of your framework names, AI tool names, or usage events are sent. The CLI transmits only a single bare "setup done" signal without project details so the dashboard install guide can mark the repository connection step. - API key storage: Your personal key is stored locally in
.pinsay/credentials.env(mode0600, hidden from git via.git/info/exclude). - Changing preferences & removal: Update your sharing consent at any time via
npx @pinsay/cli init --no-share-stack(or--share-stack). To remove all files and configurations PinSay added to your repository, runnpx @pinsay/cli remove.
Questions & resources
For security questions, privacy inquiries, or manual data purge requests, please contact support@pinsay.dev.
PinSay's hosted service is operated by PinSay, LLC, a Delaware limited liability company (registered agent address: 131 Continental Dr, Suite 305, Newark, DE 19713, USA). That address is not where your data is stored: hosted data lives in Riyadh, as described above. Full company details are in our Privacy Policy.
You can also review our legal Privacy Policy or explore our technical Documentation.
What a comment contains
Each submitted comment generates an
ElementCapturepayload containing technical identifiers of the target element. Child subtrees and parent DOM trees are not included:selectorsnapshotclassescomputedStylesappliedCssRulessourcePathparentInfopageUrl/route/pageTitleviewportWidth/viewportHeightdeviceTypedevicePixelRatiouserAgentscreenshotUrlEach comment record also contains the comment text authored by the stakeholder, the active environment tag (local, staging, or production), the authenticated author's account identifier, and any follow-up replies.