Vizno · Compliance
Information Security Policy and Procedures
Vizno's information security program - data classification, encryption, access control, infrastructure, application security, logging, personnel, vendor management, incident response.
1. Scope
This policy covers the Vizno platform (vizno.com), its supporting services, all data processed on behalf of users and creators, and all personnel with access to production systems.
2. Data classification
Vizno classifies data into three tiers:
- Restricted - authentication credentials, payout banking information, government-issued identifiers, tax forms, age-verification artifacts.
- Confidential - user account data, private project content, support correspondence, financial records.
- Internal - operational logs, analytics, non-public business records.
Restricted data is encrypted at rest, access-logged, and restricted to the minimum personnel required to operate the service.
3. Encryption
- In transit. All public-facing endpoints serve over TLS 1.2 or higher, terminated at Cloudflare. Internal service-to-service traffic between application hosts and the database runs over a private Tailscale tailnet.
- At rest. Database storage and object storage (Cloudflare R2) are encrypted at rest by the provider using AES-256. Application-level encryption is used for sensitive fields (authentication tokens, third-party API keys, payout identifiers) using keys held in environment-scoped secrets.
- Secrets management. Production secrets are stored in the Coolify environment and are not committed to source control. Access to the secret store requires production-tier credentials.
4. Access control
- All production access requires unique, named accounts. Shared accounts are prohibited.
- Authentication to production infrastructure (Hetzner host, Coolify, database) requires SSH keys or SSO; password-only access is disabled.
- User-facing authentication uses Better Auth with bcrypt/scrypt password hashing and session tokens stored server-side.
- Administrative actions in the application are gated by a separate admin role; access to the admin role is granted by the founder and audited in the application event log.
- Elevated-role accounts (admin, affiliate, creator) require email-based multi-factor authentication on every fresh session before reaching protected surfaces. See the Zero-Trust Access Architecture document for the layered model.
- Access is reviewed quarterly and revoked promptly upon role change or departure. See the Access De-provisioning Policy for the offboarding workflow.
5. Infrastructure
- Production runs on Hetzner dedicated hosts in a German data center, orchestrated by Coolify. Object storage is Cloudflare R2. The DNS, WAF, and edge proxy layer is Cloudflare.
- The database (PostgreSQL) is not exposed to the public internet; it is reachable only from the application host and from operator workstations over Tailscale.
- Backups of the database are performed daily and retained for a minimum of 14 days. Backup integrity is verified by periodic restore testing (at least annually).
6. Application security
- The application is developed in TypeScript with strict type checking. Dependencies are tracked in lockfiles and scanned continuously by GitHub Dependabot for known vulnerabilities; security updates are auto-PRed and reviewed.
- Code is reviewed before merge into the deployment branch. Production deployments are gated and tracked.
- Common web vulnerabilities are mitigated by parameterized Drizzle queries, automatic React output escaping, and CSRF protections on state-changing endpoints.
- The application baseline sends the exact headers in the table below. The main Content Security Policy is static and enforcing, which preserves static rendering and CDN eligibility without request nonces.
- Pixi, Three, and OGL are bundled application packages and receive no external source allowance. Pixi additionally runs its eval-free code path (the
pixi.js/unsafe-evalmodule, installed at both renderer entry points) so the production policy can omit'unsafe-eval'without disabling the stage renderer; a black stage canvas with a "does not allow unsafe-eval" console error means a renderer entry point is missing that import. Plaid's stable loader may frame bothcdn.plaid.comandsecure.plaid.com; its three configured API environments remain limited to connections. OpenAI Realtime is limited to/v1/realtime/calls. - Inline scripts and styles remain allowed because Next.js and the application emit them without nonces. Development alone permits script evaluation and WebSocket connections for the developer runtime; production alone requests insecure-resource upgrades.
- The Ren'Py source-player family is excluded from the main CSP rule so Next.js cannot overwrite its stricter route-generated policy. Its same-origin frame exception and token-scoped source boundary remain unchanged.
- Inbound webhooks (including Plaid, Increase, Resend) are verified against the provider's signing secret before processing.
- End-of-life software is tracked per the EOL Monitoring Policy and upgraded before vendor end-of-support dates.
Final response header table
| Header | Development | Production | Ren'Py source-player |
|---|---|---|---|
Content-Security-Policy |
Enforcing development policy below | Enforcing production policy below | Route-generated token-scoped policy below |
Content-Security-Policy-Report-Only |
Absent | Absent | Absent |
Permissions-Policy |
browsing-topics=(), camera=(), geolocation=(), microphone=(self) |
Same | Same |
Referrer-Policy |
strict-origin-when-cross-origin |
Same | Same baseline |
Strict-Transport-Security |
Absent | max-age=63072000; includeSubDomains; preload |
Same production baseline |
X-Content-Type-Options |
nosniff |
nosniff |
nosniff |
X-Frame-Options |
DENY |
DENY |
SAMEORIGIN |
The main policy has these exact directives:
| Directive | Development | Production |
|---|---|---|
default-src |
'self' |
'self' |
base-uri |
'self' |
'self' |
object-src |
'none' |
'none' |
frame-ancestors |
'none' |
'none' |
form-action |
'self' |
'self' |
script-src |
'self' 'unsafe-inline' 'unsafe-eval' https://js.ccbill.com https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs/ https://cdn.plaid.com https://challenges.cloudflare.com |
'self' 'unsafe-inline' https://js.ccbill.com https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs/ https://cdn.plaid.com https://challenges.cloudflare.com |
style-src |
'self' 'unsafe-inline' https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs/ |
Same |
font-src |
'self' data: https://cdn.jsdelivr.net/npm/monaco-editor@0.55.1/min/vs/ |
Same |
img-src |
'self' blob: data: |
Same |
media-src |
'self' blob: data: |
Same |
connect-src |
'self' data: https://api.ccbill.com https://development.plaid.com https://production.plaid.com https://sandbox.plaid.com https://api.openai.com/v1/realtime/calls ws: wss: |
'self' data: https://api.ccbill.com https://development.plaid.com https://production.plaid.com https://sandbox.plaid.com https://api.openai.com/v1/realtime/calls |
frame-src |
'self' https://cdn.plaid.com https://secure.plaid.com https://challenges.cloudflare.com |
Same |
worker-src |
'self' blob: |
Same |
manifest-src |
'self' |
Same |
upgrade-insecure-requests |
Absent | Present |
For the Ren'Py index.html response, <run-base> is the exact same-origin /api/admin/renpy-player/runs/<sessionId>/<token>/ URL. Its enforcing policy is:
base-uri 'none'; connect-src <run-base>; default-src 'none'; form-action 'none'; frame-ancestors 'self'; font-src <run-base> data:; img-src <run-base> blob: data:; media-src <run-base> blob:; object-src 'none'; sandbox allow-downloads allow-pointer-lock allow-scripts; script-src 'unsafe-eval' 'unsafe-inline' 'wasm-unsafe-eval' <run-base>; style-src 'unsafe-inline'
7. Logging and monitoring
After each CSP deployment, a human must use the staging browser console and Network panel to complete this smoke list:
- Complete sign-up through Cloudflare Turnstile.
- Open and complete the Plaid payout-link flow.
- Open Forge Monaco and confirm the Folio worker reports diagnostics.
- Start Brain Dump voice capture and confirm OpenAI Realtime connects.
- Exercise the player Pixi stage.
- Load the creator OGL hero.
- Load the admin Three pose editor.
- Preview blob-backed image, audio, and video media.
- Launch the Ren'Py iframe and confirm its game assets load.
- Confirm main responses carry
Content-Security-Policyand omitContent-Security-Policy-Report-Only. - Confirm the Ren'Py
index.htmlresponse carries its token-scopedContent-Security-PolicyandX-Frame-Options: SAMEORIGIN.
If enforcement blocks a required staging flow, roll back the CSP commit or temporarily change only the main CSP header key back to Content-Security-Policy-Report-Only and deploy. Keep every non-CSP baseline header active, capture the exact violation, add only the narrow source or capability it proves necessary, rerun the security-header check, production build, and package suite, then repeat this smoke list before enforcement returns. Never replace the policy with a wildcard or disable it globally.
- Application errors and security-relevant events (authentication, administrative actions, sensitive data access) are written to a tamper-resistant audit log.
- Logs are retained for a minimum of 90 days.
- Anomalous activity (failed authentication bursts, unexpected admin actions) triggers alerts to the security contact.
8. Personnel
- All personnel with access to production systems agree to confidentiality terms and the acceptable-use elements of this policy.
- The company maintains a minimal team; access is granted on a need-to-know basis and revoked immediately upon role change or departure.
- Personnel are required to use full-disk encryption, screen-lock, and a password manager on any device used to access production.
9. Vendor management
Vizno relies on the following primary subprocessors: Hetzner (compute), Cloudflare (DNS, edge, object storage), Plaid (bank verification), Increase (US ACH payments), Payoneer (international payments), Resend (email), Anthropic (AI). Each is selected on the basis of its published security posture and contractual data-protection commitments.
10. Incident response
In the event of a suspected security incident:
- The security contact is notified immediately and acknowledges within one business hour during business hours.
- Affected systems are isolated and a written incident record is opened.
- Root cause is identified, scope of impact is determined, and remediation is applied.
- Affected users and any subprocessors implicated by the incident are notified in accordance with applicable law and contractual obligation.
- A post-incident review is conducted and findings are recorded.
The security contact for incident reporting is security@vizno.com.
11. Acceptable use
Personnel must not access user data except as required to operate, support, or improve the service; must not export Restricted data to unmanaged devices; and must not share credentials or bypass access controls.
12. Policy maintenance
This policy is reviewed annually by the founder and updated as the platform's infrastructure, team, or regulatory obligations evolve. Material changes are recorded with a revision date.