For your security team

Here is how OneHub360 keeps your data safe.

The hosts, the headers, the hashes, the audit log, and the audits we have not done yet. If you are running diligence on us, start here.

Isolated at the database

FORCE row-level security on every customer table. Wrong tenant, or no tenant, returns nothing.

Keys hashed at rest

SHA-256 before storage, plaintext shown once, each key bound to a single business.

Every action traceable

A per-tenant audit trail on API and AI-agent changes, in Settings. The same data we use to debug, so you can prove what happened.

01

Hosting and infrastructure

Your data and our other customers' data live in one PostgreSQL database, but every tenant's rows are walled off from every other tenant by the database itself (see the next section). We are deliberate about the perimeter and the backups.

Encrypted backups
A logical database dump runs on a regular schedule, is gzipped, size-checked (a truncated dump is treated as a failure), and rotated. Backups are encrypted and mirrored off the host, and we test restoring from them.
TLS enforced, auto-renewed
HTTP traffic is redirected to HTTPS. Certificate renewal runs unattended, and we monitor expiry separately.
Security headers
Per-route Content-Security-Policy headers. X-Frame-Options: DENY on all non-Shopify-embedded routes (Shopify embeds use frame-ancestors CSP instead so the admin can iframe legitimately).
02

Tenant isolation, enforced by the database

This is the control we lead with. Tenant isolation in OneHub360 is not a convention developers have to remember or a WHERE clause they might forget. It is enforced inside PostgreSQL by FORCE row-level security on every application table. Even if application code asked for the wrong tenant's rows, the database would refuse to return them.

FORCE row-level security on every table
Every application table runs FORCE ROW LEVEL SECURITY in PostgreSQL, governed by hundreds of policies. The application connects as a role that cannot read or write a tenant's row unless the current request has set per-request, transaction-local identity (the user, tenant, and business). A query with no principal set, or the wrong one, returns nothing and a write is rejected by the database. Misses fail closed.
Request identity is transaction-scoped, not ambient
On every authenticated request we open a transaction and set the tenant and business identity as transaction-local settings before any query runs. Those settings are what the row-level-security policies check. They cannot leak between requests, and a background job or webhook must explicitly run under a named system principal to touch data at all.
Tenant isolation, hard-bound to API key
API authentication resolves the tenant from the key on every request. Handlers receive a result with the tenant fixed; no handler can override it, and the same row-level-security policies still apply underneath. A leaked key is scoped to one tenant only.
Mutation guards on every write
A shared guard wraps every mutating route. It records an audit event (regardless of outcome), supports Idempotency-Key headers (24h cache, refuses key reuse with a different body), and maps database constraint errors to correct HTTP semantics (409 on conflict, 404 on missing, 400 on bad reference).
Plan-tier gating, server-side
Free-tier requests get a 403 PLAN_UPGRADE_REQUIRED before any tool registers. No way to spoof a plan from the client. The MCP layer renders the 403 as an upgrade CTA; REST callers get the legacy error envelope unchanged.
API keys: SHA-256 hashed at rest
API keys are hashed with SHA-256 before storage; we never persist the plaintext. The key is shown once at creation and never again. Revocable from Settings, and revocation propagates within about a minute.
Confirm-flag pattern on destructive actions
oh360_delete_contact, oh360_send_proposal, and oh360_send_invoice require confirm:true at the MCP layer. Without it, the call is refused before any HTTP roundtrip. Agents cannot accidentally delete or send by hallucinating a tool call.
NextAuth sessions, 30-day JWT
Sign-in passwords are hashed with bcrypt. Built-in CSRF protection. HTTP-only cookies. Session cookie maxAge: 30 days, refreshed on activity.
Per-IP and per-key rate limits
High-volume routes (chat, tracking heartbeat) carry their own bucket sizes. Public form submissions are throttled per IP and per email. API keys carry daily quotas by plan tier.
03

Data

PostgreSQL, with row-level security enforced on every table and indexes and transactions tuned for the working set we run. The interesting parts are the isolation guarantees, the indexing, and what we do not store at all.

PostgreSQL with row-level security
The database is PostgreSQL and is never exposed to the public internet. Every application table enforces row-level security so a query can only ever see the requesting tenant's rows.
Indexed and transactional writes
Foreign-key and lookup columns are indexed so contact-search and pipeline queries stay fast as a book of business grows. Critical multi-step writes run inside transactions so a partial failure rolls back cleanly rather than leaving inconsistent data.
Error monitoring with PII scrubbed
Application errors are reported to our monitoring with sensitive fields scrubbed before they leave the server: email addresses, phone numbers, and arbitrary PII strings are replaced with truncated SHA-256 hashes so we can correlate an error without leaking identities.
Analytics consent-gated
No analytics or advertising technology fires until the visitor accepts the cookie banner. Consent defaults to denied for analytics and ad storage on first paint; consent updates are pushed through only after the visitor opts in.
04

Authentication and access

Two auth paths: session (people) and API key (programs). They unify into the same ApiAuthResult so handlers do not branch on auth method. Cross-business access is never implicit.

NextAuth, JWT-based, 30-day sessions
Cookie sessions, signed and HTTP-only. Sign-in is email + password (bcrypt-hashed). CSRF tokens enforced on every state-changing form.
Multi-business, explicit membership
A user can belong to several businesses; each membership is an explicit record. There is no implicit cross-business access: a user only sees businesses they have a membership for, and the database's row-level security enforces that scope too. Roles (Owner, Admin, Member) are enforced by the app.
API keys for programmatic access
Separate auth path. Keys carry a businessId; they cannot be repointed at a sibling business in the same tenant by the API consumer. Keys are minted at /settings/api-keys, revoked at the same place.
05

Audit log

Every change made through the API or an AI agent lands in a per-tenant audit trail. You can see who did what, with what arguments, and what happened. We use this internally to debug; you see the same data in Settings.

Per-tenant audit trail in Settings
Each event records the tenant, business, the user or API key that made the call, the authentication method, the method and path, the tool name where applicable, PII-redacted arguments, the outcome status and error code, the duration, and any idempotency key. The trail is indexed for fast filtering by tenant, key, business, and tool.
Tenant-scoped, no cross-team visibility
You see your tenant's events. Nothing else.
Mutations logged regardless of outcome
4xx and 5xx responses generate audit events too: a failed delete attempt is recorded with the error code (NOT_FOUND, DUPLICATE, etc.). You can prove a destructive action did not succeed, not just that it was attempted.
06

Compliance and privacy

We honor the requests required of us under GDPR, CAN-SPAM and PCI scope. The last section lists the audits we have not done yet.

GDPR: data export and deletion by request
To export your data or delete your account and its associated data, email support@onehub360.com. We fulfill verified requests, including right-to-be-forgotten requests. Self-serve export and deletion controls are on the roadmap.
CAN-SPAM: List-Unsubscribe on every campaign email
Every outbound campaign carries List-Unsubscribe and List-Unsubscribe-Post headers (RFC 8058 one-click). Unsubscribed contacts are filtered automatically, so campaigns cannot send to them by mistake.
PCI: cards never touch our infrastructure
Card payments run through our card processor. We store the payment reference and, where applicable, the last 4 digits the processor returns. Card numbers, CVVs and expiration dates never enter our database.
Cookie consent banner, default denied
consent_default is denied for analytics_storage, ad_storage, ad_user_data, and ad_personalization until the visitor accepts. Only essential cookies (session, CSRF) load before consent.
07

Vulnerability disclosure

If you find a security issue, please tell us. We are happy to coordinate a disclosure timeline, credit you in our acknowledgments, and treat the report with professional courtesy.

security@onehub360.com
Email us with the details.
We confirm receipt
We reply to confirm we have your report. Triage and remediation timeline depend on severity; we will tell you what we are doing and when.
Coordinated disclosure preferred
We will work with you on a public-disclosure timeline that gives our customers time to be safe. Please give us a chance to fix it before full disclosure.
08

What we do not do (yet)

Where we stand on the certifications you may be checking for.

SOC 2: not audited yet, on the roadmap
We have not had the audit. When we do, this page will say so and link to the report.
HIPAA: no
OneHub360 is not designed for protected health information. We do not sign BAAs. If your use case involves PHI, we are not the right tool.
ISO 27001: not certified, on the roadmap
We have not had the formal audit.

Questions your security team needs answered?

Email security@onehub360.com. A person reads every message. We are happy to walk through the architecture, fill in a questionnaire, or get on a call.