Engineering
When the software does not know, it does nothing.
One rule holds together every product Doyel Labs ships. The thing that can spend money — cash, a wage, a share — lives on your machine, and the thing we run in the cloud cannot spend it. Everything below follows from that.
Rule 1
Fail closed.
An unknown value blocks an action rather than filling it in. Nothing here has a fallback that pretends. If the wage determination for a contractor's county is not on SAM.gov, the draft is refused. If a broker quote is stale, the entry is not placed. If a Space code has expired, the join call returns permission-denied. An outage should look like an outage.
Payroll
A day rate below the SCA floor blocks the draft. Every stub carries the floor line.
BAI
A stale quote, a missing account snapshot, an unknown earnings date each block an entry; chat cannot spend.
ConnectionLoop
Firestore and Storage rules default-deny. A member row is created only against a live invite code.
Rule 2
Local-first for anything that can move money.
A cloud breach cannot reach a broker account we cannot see. Broker credentials for BAI stay in Windows Credential Manager. The device holds the arm phrase; we hold none of them. On the payroll side, we store the operator's records — but we do not hold their bank routing or account numbers, and we do not move their money.
BAI
Broker keys and access tokens live on the operator's machine, encrypted where the OS allows. Never sent to Doyel Labs.
Payroll
Bank routing and account numbers are not stored. The operator pays through their own bank; the software prepares the pay run.
ConnectionLoop
Sessions live in the OS keychain (Keychain / Keystore). The on-device state cache is AES-256-GCM sealed under a per-install key.
Rule 3
Updates are signed and refused mid-trade.
No one, including us, can push code to your desk that we did not sign with a key baked into the app. An update is refused while the desk is armed or holding a position. The website ships as static assets on Cloudflare Pages; there is no runtime we can push mutations to.
BAI
Signed with a key baked into the app. Refused while armed or holding a position.
Website
Static export, immutable per deploy on Cloudflare. `_headers` bans third-party scripts on marketing pages.
Rule 4
Structured logs, redacted at the source, one row per action.
Log lines carry ids, counts, and durations — not content. A redaction pass in CI drops any field that looks like a secret or a wage. Every state change worth reading a year later becomes an audit row: a sign-in, a rate change, a passkey enrolment, an armed session, a blocked stub. Rows export to CSV.
Payroll
Paystub, WD lookup, SCA-block, contractor-rate change, email delivery — all logged with actor and detail. 180-day retention.
BAI
Sign-in, device change, billing event, staff action. Structured logs with a redaction pass tested in CI.
ConnectionLoop
Callables log ids, counts, codes, durations — never content. Crash notes are scrubbed twice.
Rule 5
Tests pin the paths a subtle regression could break.
Chat cannot spend
SCA floor is enforced
Rules v2 is exercised
Kill disarms, never sells
Subscription failure is not a cliff
Update refused while armed
The company decision
Say what the software does not do, in the same voice.
Doyel Labs LLC is not a broker-dealer, an investment adviser, a bank, a payroll processor, a professional employer organization, a money transmitter, or a reporting agent. None of the software above holds customer money or securities. We ship one company. We say the same thing everywhere.
Source of truth · BAI-Desk/website/src/app/security/page.tsx · ConnectionLoop/docs/SECURITY.md · index/netlify/functions/payroll.js