Security · state of the running system
Every gate here fails shut.
A missing key, an unrecognised action or an unreachable database stops the platform and waits for a person. This page lists the controls that run today, each one traceable to a line of code or a live response, and the ones that do not exist yet.
Live posture
Fail-shut
Fail-shut, in five conditions
Operator key unset
Every session cookie is rejected
Trigger secret unset
Scheduled jobs answer unauthorized
Action missing from the matrix
Human approval required
Approval expired
The decision is refused
Governance database unreachable
Outbound write stops
How the protection works
Twenty-four controls, one page.
Each row describes a mechanism that exists in the running system. Where a control belongs to the hosting provider, the provider is named in the row.
01 · Access
Who gets in
05 controls
Signed session cookie
The operator cockpit accepts one cookie: an HMAC of a versioned string computed with a server-side key, set httpOnly and marked secure over https. A deployment running without that key rejects every cookie presented to it.
Gated APIs answer 401
Operator endpoints verify the session themselves, on every host the deployment serves, before they touch the database. A request without one gets 401 and no payload — the answer /api/leads/list gives on unistaff.ai right now.
Rotation ends every session
The cookie is derived from the key and a version string, so changing either makes every cookie issued earlier fail verification. One rotation signs everyone out at once.
One answer at the login
An empty key and a wrong key return the same 401. The comparison hashes both sides to a fixed length first, then walks the bytes without an early exit, so timing and length give nothing away.
Machine triggers carry a secret
Scheduled and machine-triggered jobs present a bearer secret. With no secret configured, the endpoint reports an unauthorized state and stays closed.
02 · Customer data
Where the data sits
07 controls
One deployment, one customer
Separation runs on the deployment boundary: its own database, its own operator key, its own credential key. The schema holds no tenant, organisation or workspace column splitting one shared table between customers, so a row belonging to another business has no route into a query.
Encrypted in transit
Requests travel over HTTPS on TLS 1.2 or 1.3 with forward-secrecy ciphers, and responses carry HSTS with a two-year max-age. Neon requires TLS on database connections as well.
Encrypted at rest
Neon keeps the database on storage encrypted with AES-256 and retains encrypted backups for 30 days. That encryption is the provider's platform default, applied by Neon underneath our application.
Platform credentials sealed before storage
An ad-platform token is encrypted with AES-256-GCM under a key held in the environment before it reaches the database; the row keeps ciphertext, a random 12-byte initialisation vector and the auth tag. With the key missing, the store raises an error and writes nothing.
Lead records leave the cockpit masked
Outside an operator session, a lead reads as an initial plus the names of the channels the person offered. Names, phone numbers, email addresses and free-text answers stay in the database.
Logs hold ids and outcomes
Notification logging emits a lead id, an outcome code, or the names of unset environment variables. Database failures return a fixed message to the caller, so driver text that can echo submitted values stops at the server boundary.
Every query value is bound
Filters, ranges and page sizes travel as bound parameters while the SQL text carries placeholders. An injection attempt stays what it started as — a string sitting in a parameter slot.
03 · Public surface
What the open endpoints accept
04 controls
Body cap counted in bytes
The two public form endpoints stop at 10 KB and the operator login at 4 KB, measured in bytes before any parsing, so multibyte text cannot slip past a character count. An oversize post to the contact endpoint comes back 413.
Bot trap answers like a real submission
A hidden field sits in the form, invisible to a person and attractive to a bot. When it arrives filled, the response carries the same 200 and a freshly minted id, and the database stays untouched.
Page size clamped on the server
List endpoints clamp the requested page size into range before it reaches the query, so a crafted URL returns one page and stops there.
Platform DDoS mitigation
The application runs on Vercel, whose platform-wide firewall applies automatic DDoS mitigation to every deployment with no configuration on our side.
04 · Autonomy limits
What the AI is allowed to do
08 controls
Kill switch, read before the write
The switch is the first gate on every outbound write to a connected ad account, checked ahead of the mutation. Engaging it takes a written reason, which the record keeps. A blocked request holds its approved state and waits.
Spend caps stop the mutation
Each write compares its daily spend against the daily and monthly caps and throws when it exceeds them. A cap left unset has nothing to compare against and the gate passes it, which is why the approval gate below carries the weight until you set one.
An unlisted action waits for a person
The autonomy matrix answers “requires approval” for any action with no rule of its own, so a new capability arrives gated and stays that way until someone writes the rule.
Writes run against an approved request
A mutation executes when an approved change request matches it exactly, tied to a recorded decision with its timestamp and note. Anything else raises approval_required and goes nowhere.
An expired approval stays expired
A pending item that runs past its clock keeps its status in the database and gets refused at decision time. A decided item resists a quiet second decision: the update carries the pending state and the expiry inside its own condition.
Objects are created paused
Campaigns, ad sets and ads made through the write path come into existence paused, so a new object spends nothing until a person starts it.
Journal of runs and decisions
Agent runs, sync attempts, change requests and approval decisions land in tables with timestamps, parameters and outcome, failures included. One caveat worth stating plainly: the actor on a decision is a label supplied by the caller, since operator access today is a shared key.
No database, no write
Gate checks read from the governance database. When it is unreachable, the write raises enforcement_unavailable and stops — a client without a database can mutate nothing.
The access, masking, encryption and gating paths above are pinned by an automated suite: 788 tests, 0 failures on the last run. The tests state the closing direction as clearly as the opening one — a rotated key invalidating every issued cookie, a forged cookie refused, a tampered auth tag throwing, and no outcome putting personal data into a log line.
Today and later
What runs today, and what does not exist yet.
The right-hand column is the whole list as of today, printed in full so a procurement checklist can be answered in one pass. Each line there is a control this platform lacks; the date any of them arrives stays unset until it ships.
Running today
- Encryption in transit
- TLS 1.2 / 1.3 · HSTS 2 years
- Encryption at rest
- AES-256 · Neon default
- Database backups
- 30 days, encrypted · Neon
- Operator session
- Signed cookie, fail-shut
- Machine triggers
- Bearer secret, fail-shut
- Lead masking outside the cockpit
- Initial + channel names
- Stored platform credentials
- AES-256-GCM
- Outbound write gate
- Approved change request
- Kill switch
- 3 scopes, reason required
- Spend caps
- Daily + monthly, checked per write
- Action journal
- Timestamps, outcomes, failures
- Automated test suite
- 788 tests, 0 failures
Not built yet
- SOC 2 report for Unistaff
- ISO 27001 / ISO 27701 for Unistaff
- HIPAA controls and a signed BAA
- GDPR DPA, subprocessor list, standard contractual clauses
- SSO, SAML, OIDC, SCIM provisioning
- Per-user accounts, roles and permission scopes
- Multi-factor authentication on operator login
- Row-level separation between customers inside one database
- Rate limiting and lockout on public endpoints
- CSP and the other security response headers
- Third-party penetration test of this application
- Bug bounty and a published disclosure policy
- Data residency selection
- Retention purge and TTL enforcement
- Self-serve data export and deletion
- Customer-facing audit-log search and export
- Column-level encryption of lead contact data
- Per-session revocation
- CAPTCHA or a managed bot-defence service
- Uptime SLA and a public status page
- Key management service and automated secret rotation
- Documented restore drill of our own data
On provider certifications · Unistaff holds no certification of its own. Our hosting providers hold theirs and publish them on their own trust pages, where they cover those platforms. A claim that this application is certified would be false, whoever repeated it — including if the sentence borrows a provider's badge to say it.
How to request details
Every claim here has a source behind it.
Name the control you need to check and the answer points at what backs it: the file, the migration, the provider document, or the live response you can reproduce yourself. When a control sits in the unbuilt column, the answer says that in the first line.
- Security email
- hello@unistaff.aiOne inbox for questionnaires, pilot reviews and anything on this page you want to verify.
- Reporting an issue
- Same address, subject line “security”A published disclosure policy and a bug bounty sit in the unbuilt column, so the route today is the inbox.
Questions procurement asks
Frequently asked questions
A full lead record has two doors: an operator session or a bearer secret. The API verifies one of them itself before it touches the database, independent of the page gate. Every other caller receives a masked record — an initial and the names of the contact channels, with the name, phone, email and free-text answers left in storage.
Traffic runs over HTTPS on TLS 1.2 or 1.3 with forward-secrecy ciphers, and responses carry HSTS with a two-year max-age. Neon requires TLS on database connections, keeps the data on AES-256 encrypted storage, and retains encrypted backups for 30 days. The at-rest encryption comes from the provider's platform default; our application code adds no encryption layer of its own on top of it.
No. Unistaff holds no certification of its own, and none is in progress. Our hosting providers publish their own certifications on their own trust pages; those cover their platforms and say nothing about this application. Treating a provider's audit as an audit of Unistaff would be wrong, and we say so before a procurement review has to ask.
Separation runs on the deployment boundary: a deployment carries its own database, operator key and credential key. The schema holds no tenant, organisation or workspace column, so row-level separation inside one shared database stays absent today. Ask which deployment your pilot would run in before you plan around it.
Four gates sit ahead of every outbound write to an ad account: the kill switch, the daily and monthly spend caps, the autonomy matrix, and a matching approved change request. An action missing from the matrix requires human approval by default, an expired approval stays expired, and an unreachable governance database stops the write where it stands. One limit worth stating: a cap you have not set gives the caps gate nothing to compare against, so it passes and the approval gate is what holds the spend.
Those three do not exist today. Operator access is a single shared key with no per-user accounts, MFA or SSO; the journals are internal tables with no customer-facing search or export; and no DPA, subprocessor list or standard contractual clauses have been drafted. Write to hello@unistaff.ai before you build a rollout plan that depends on any of them.
The record is written first, then the notification is attempted with a five-second timeout. A delivery failure is recorded as an error line carrying the lead id and an outcome code, with the submitted values and the mail provider key left out of the log entirely.
The rest of the questions — pricing, launch, connectors, metrics — are grouped in the AI employee FAQ, and the data answers there point back to this page.