Security & privacy
What protects your workspace, and what we have not built yet
Kharazm holds ordinary but sensitive material: who owns a program, what is behind, which risks are open, and what stakeholders were told. This page describes the controls enforced in code, where data is hosted, and the gaps we know about. It is written for a procurement reviewer, not only an engineer.
We hold no compliance certification and have not commissioned a third-party penetration test. We would rather tell you that here than let you discover it in a questionnaire.
Identity and sessions
Signing in is the only way into a workspace. Everything below happens on the server; nothing about your identity is decided by the browser.
- Passwords are hashed with scrypt using a 16-byte random salt generated per user. Verification is constant-time, so the comparison itself does not leak information about the stored value. Plaintext passwords are never stored and never written to logs.
- Sessions use a 32-byte random opaque token. Only the SHA-256 hash of that token is kept in the database, so a database leak does not hand an attacker usable sessions. The token itself lives in a cookie that is
httpOnly(unreadable from JavaScript),SameSite=Lax, andSecurein production. - Sessions expire after 14 days and are deleted server-side on sign-out, so signing out actually ends the session rather than just clearing a cookie.
- Optional two-step sign-in. Each person can turn on a second sign-in step using an authenticator app (time-based one-time codes, RFC 6238). Turning it on gives ten single-use recovery codes, shown once and stored only as hashes. The authenticator secret is encrypted at rest with AES-256-GCM under a key held outside the database. A code cannot be replayed, a sign-in attempt allows five codes within five minutes, and turning the feature off needs the password and a current code. It is per person and optional: an organization cannot yet require it of its members.
- Email addresses are verified. A first-workspace request is created only after the requester opens a confirmation link sent to that address. Other accounts are sent a confirmation link too, and completing a password reset also confirms the address.
- Sign-in activity you can see. Each person can review their own recent account activity — sign-ins, failed attempts, two-step changes and password resets — with an approximate network (IPv4 truncated to /24, IPv6 to /48) rather than a full IP address. These records are deleted after 13 months.
- Rate limiting is applied per IP address to authentication and high-impact write routes: sign-in and two-step code entry (20 attempts / 5 minutes each), two-step setup changes (10 / 15 minutes), registration (8 / 10 minutes), email confirmation (20 / 15 minutes), invitation acceptance (12 / 15 minutes), initial-operator bootstrap (5 / 15 minutes), forgot-password (6 / 15 minutes), reset-password (10 / 15 minutes), workspace creation (8 / hour), invitation creation (40 / hour), data exports (10 / hour), account and workspace deletion (5 / hour), operator decisions (60 / 5 minutes), and general domain writes (300 / 5 minutes). The bounded limiter is instance-local, not a distributed edge control.
- Password reset issues a 32-byte random token, stored only as a SHA-256 hash, valid for one hour and usable once. Completing a reset revokes every existing session for that account — the equivalent of "sign out everywhere". It does not switch off two-step sign-in. The forgot-password endpoint returns an identical response whether or not the address has an account, so it cannot be used to discover which staff are registered.
- Invitations are email-bound and single-use, and need the invitee's confirmation. Only a SHA-256 token hash is stored. An invitation expires after seven days, can be revoked by an admin, and is consumed atomically with the profile, credential and membership changes needed to join. Someone who already has an account signs in and confirms before joining another workspace, without creating another credential.
- Bearer links use URL fragments. Invitation, confirmation and password-reset tokens appear after
#, so they are not sent in the HTTP request URL, and the application removes them from browser history as soon as it reads them. - Expired authentication records are cleaned up opportunistically. On API traffic, the process runs cleanup at most once an hour: expired sessions, reset records, two-step sign-in attempts and email-confirmation links are removed, sign-in activity older than 13 months is removed, first-workspace requests older than 30 days are removed, and expired invitations are removed 30 days after expiry.
Authorization and tenant isolation
The organization is the intended security boundary. Server queries and writes are scoped to the organization in the authenticated session, and automated tests cover cross-tenant isolation.
- Server-authoritative permissions. Every write passes through a single shared reducer whose capability check runs on the server, with the acting user taken from the session — never from the request body. A client cannot claim to be an admin. Permission checks in the interface exist to hide buttons that would fail; they are advisory, and the server decides.
- Every request is scoped to the session's active organization. Cross-tenant isolation is covered by automated tests, so a regression that let one organization read another's programs would fail the build.
- Append-only audit log. Every change to workspace records is logged with who made it, when, a request id, and what changed — the before and after values of each field. A database trigger rejects any attempt to edit or delete a log entry; entries are removed only when the whole workspace is deleted. Admins can read the log in the app, download it as CSV, and it is included in the workspace export.
- Award-scoped viewers. A viewer invitation can be limited to chosen awards and given an access end date. For such a viewer the server cuts the workspace down before anything reaches the browser: the chosen awards, their subrecipients, monitoring and evidence, the basics of the programs they belong to, those programs' published updates and recorded decisions, and the names of the people those records cite. Tasks, drafts, comments, portfolios and the team roster are not sent. Access stops automatically at the end date.
- Record protection. Published updates and monitoring events cannot be deleted, and awards and subrecipients cannot be deleted once records hang off them (subrecipients or evidence on an award; monitoring history or evidence on a subrecipient). The application refuses, and database constraints back it up. A program with an award or published updates can only be archived, a closed monitoring finding cannot be rewritten, and a published update freezes its completed-task titles, its evidence index and the publisher's name. This is application and database enforcement, not write-once (WORM) storage: someone with direct database access could technically alter data, and an admin can deliberately delete the whole workspace.
The four access levels
Roles are assigned per person, per workspace, and are enforced on the server for every action.
| Level | What it can do |
|---|---|
| Admin | Everything a manager can do, plus create and delete programs, delete an award or subrecipient entered by mistake, and manage the people in the workspace. Admins see and download the audit log, export the workspace, and can delete it. |
| Manager | Create and delete tasks; manage programs and milestones; record awards and subrecipients; publish updates; remove evidence links. |
| Member | Edit tasks, change status and comment; log risks, issues and decisions; log monitoring events, close findings and add evidence links. |
| Viewer | Read-only. An invitation can limit a viewer to chosen awards and set an access end date, which suits an auditor or a funder. Any member, viewers included, can generate an award's audit pack. |
A person can join an existing workspace only through an invitation with a role already assigned. A signed-out person's initial registration request enters an operator approval queue once they confirm their email address; an already signed-in user can create an additional workspace and becomes its admin without another review. A submitted email address never grants operator authority: the first configured operator must be provisioned through a deployment-controlled, one-time bootstrap secret. The FAQ sets out the exact paths.
Transport and browser hardening
The headers below are set by the application, not left to a default configuration.
- HTTPS is enforced in production, with HSTS asserted on TLS requests:
max-ageof two years,includeSubDomains, andpreload. - A strict Content-Security-Policy with
default-src 'self'. There are no third-party script or connect origins,frame-ancestorsis'none', andobject-srcis'none'. The marketing site you are reading now runs under the same policy, which is why it loads no external fonts, scripts or images. - Supporting headers:
X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy: same-origin, aPermissions-Policythat switches off camera, microphone and geolocation, andCross-Origin-Opener-Policy: same-origin. - CSRF defense in depth: SameSite cookies plus a same-origin guard that rejects cross-origin requests using unsafe HTTP methods.
- Schema validation covers authentication, bootstrap, workspace, invitation and operator-decision writes. All 39 supported domain operations use a strict discriminated Zod union with operation-specific ID formats, enums, ISO dates, text lengths and array limits; unknown keys are rejected at the envelope and payload levels. The domain reducer separately rechecks permissions and referential integrity.
Privacy posture
The browser loads Kharazm from one origin and makes no direct connection to analytics, advertising or other third-party services. The server does use the infrastructure, email and billing providers described below.
- No analytics, no trackers, no ad tech, no session recording. The CSP's
connect-src 'self'forbids them at the browser level, so this is enforced rather than promised. - The web font is self-hosted and served from the same origin. No font CDN sees your users' IP addresses.
- Data minimization. We store an account email, a display name, organization membership and role, the program data your team creates, a billing record per workspace (plan, cycle, status, billing dates and Stripe identifiers), each person's sign-in activity with an approximate network, and — only for people who turn on two-step sign-in — an encrypted authenticator secret and hashed recovery codes. Plaintext passwords, message bodies, recipient addresses, reset/invitation tokens and actionable links are not written to production application logs.
- The application is login-walled, and
robots.txtkeeps crawlers away from the API surface. - No built-in accounts or demo workspace. The product ships no seeded credentials or sample content. Initial registration is operator-reviewed, while additional workspaces created by signed-in users are not separately reviewed.
- No AI or agentic processing today. Those capabilities are a plan for the next product workstream, not a current feature. Kharazm does not currently send workspace content to a model provider or use it to train models.
- Secrets stay out of the repository. The database URL and the key that encrypts two-step secrets are injected as platform secrets and are never committed; local state directories are excluded from both the repository and the container image.
Full detail on what we collect and why is in the privacy policy.
Your data: export and deletion
These are self-serve, in the app. You do not need to email us for any of them.
- Workspace export. An admin can download the whole workspace as JSON: every record, the members, the invitation history, the audit log and a billing summary.
- Personal-data export. Anyone can download what Kharazm holds about them: profile, account, memberships, comments, sessions and sign-in activity.
- Account deletion. Anyone can delete their own account with their password. Their sign-in, memberships and sessions are removed and their profile name becomes "Deleted account". Records they created stay in the workspace, which belongs to the organization. It is refused while they are the last admin of a workspace others use, or its only member.
- Workspace deletion. An admin can permanently delete the workspace and everything in it, including its audit log, by entering their password and typing the workspace's name.
When an admin removes someone from a workspace, that person's name stays on the records they created.
Kharazm uses transactional email for email-address confirmation, password resets, workspace invitations, new-workspace approval notices, and a reminder to a workspace's admins three days before its trial ends. Messages are delivered through Resend using a server-side HTTPS request, with no third-party SDK or script embedded in the browser.
If no Resend API key is configured, an administrator can still copy the shareable invitation link returned in the interface. Confirmation, password-reset and approval emails cannot be delivered, so a production deployment should configure email before accepting accounts.
Billing
Paid plans are billed through Stripe. When an administrator chooses a plan under Plan & billing, Kharazm redirects them to Stripe-hosted Checkout. Card and bank account (ACH) details are entered on Stripe's pages and never reach Kharazm's servers. Because Checkout is a redirect rather than an embedded form, no Stripe script runs on a Kharazm page and the Content-Security-Policy described above is unchanged. Cancellation, payment-method updates, invoices and plan changes happen in the Stripe customer portal, opened from the same screen. An organization that needs an invoice to pay by check or bank transfer can ask us for one instead.
Stripe receives the billing administrator's email address, the workspace's internal identifier, the chosen plan and billing cycle, and the payment details entered on its own pages. It does not receive programs, awards or records. The privacy policy lists what Kharazm itself keeps.
Where your data lives
Workspace content — programs, awards and records — is stored and processed in the United States, on every tier of the deployment, and this is enforced in code rather than promised in prose. The exceptions are named below: billing data is processed by Stripe, and email is delivered through Resend.
The application runs on Fly.io with machines pinned to San Jose, California. Managed PostgreSQL is provided by Neon in a United States region, over a connection that requires TLS.
The database region is a boot invariant, not a configuration convention. Kharazm validates DATABASE_URL at start-up against an allowlist of United States region identifiers, and a production process refuses to start if the configured database cannot be positively proven to sit in a US region. The check is deliberately a positive allowlist rather than a list of forbidden regions: cloud providers add regions constantly, so “not on a list of foreign regions” proves nothing, while “matches a known US region” does. A hostname that cannot be verified is treated as unproven and rejected. An operator cannot silently move the database abroad — the deployment stops.
The application does not send workspace content to another service. There is no data warehouse, analytics pipeline, product integration or model provider. Stripe receives only the billing details listed above, and Resend only the messages being sent. Workspace storage, logs and backups remain within the disclosed United States hosting and database providers.
Three operational details, so the commitment is precise rather than merely confident. Fly.io serves the site over an Anycast network, so for a visitor outside the United States the encrypted connection is accepted at the network edge nearest to them and carried to the US machine; your data is stored and processed only in the United States, while the encrypted transport for a foreign visitor's own request naturally crosses their own region on the way in. And outbound transactional email leaves by design: when a password reset or invitation is sent, it is delivered to the recipient's own mail server, wherever that recipient chose to host it. And billing data is processed by Stripe, an independent payment processor that may process it outside the United States under its own terms. None of these affects where workspace content lives.
Kharazm is self-contained and can be deployed for a single organization if your policy requires the data to sit somewhere specific. Write to hello@bakaroart.com and describe the constraint you are working under.
Zero trackers, self-hosted assets, no third-party analytics
Every byte the browser loads comes from kharazm.org. There is no third-party analytics, no product-analytics SDK, no session recording, no heatmap, no advertising or conversion pixel, and no A/B testing service. We do not set an advertising identifier and we do not participate in any cross-site tracking scheme.
Fonts are compiled into the deployment and served from our own origin rather than from a font CDN, so loading a page does not disclose your visit to a font host. Stylesheets, scripts and images are served from the same origin. There is no tag manager, so nothing can be added later without a code change that is reviewed and deployed.
This is enforced by a strict Content-Security-Policy and by Cross-Origin-Resource-Policy: same-origin, not merely by our intent: the browser itself refuses to load a third-party script or beacon from a Kharazm page. Apart from its own database, the server makes outbound requests to exactly two destinations in normal use, both server-side: Resend's API, only when transactional email is configured and only with the contents of the message being sent; and Stripe's API, only for billing and only with the billing details described above. Checkout itself is a redirect to a Stripe-hosted page, so card data never reaches Kharazm servers. If the operator enables error alerts, a third request goes to the operator's own chat webhook, carrying only an error type, the affected route and a request id — never workspace content or personal data.
The practical consequence for a grant-funded organization: opening Kharazm does not disclose your beneficiaries, your programme names or your staff's browsing to an advertising network, and there is no third party to add to your own subprocessor disclosures because of the browser session.
What we have not done yet
This section exists because a security page without one is not worth reading. These are real, tracked gaps, stated plainly.
- No third-party penetration test or security audit. No independent firm has tested Kharazm. The controls above are implemented and tested by us; they have not been attacked by a specialist.
- No compliance certification of any kind. We are not SOC 2 audited, not FedRAMP authorized, and we make no HIPAA or GDPR compliance claim. If your procurement process requires one of those, Kharazm does not meet it today, and we will not tell you otherwise.
- Two-step sign-in cannot be required, and there is no single sign-on. Two-step sign-in is optional and per person; an organization cannot yet make it mandatory for its members. There is no SSO, SAML or SCIM.
- Off-box and point-in-time backups depend on the database plan. Backup and recovery guarantees come from the underlying PostgreSQL plan rather than from anything Kharazm adds on top. If a specific recovery objective matters to you, ask us before you commit and we will tell you exactly what the current plan provides.
- No configurable retention policy or legal hold. The server cleans up limited authentication records and sign-in activity on the schedule described above. Program content and the audit log otherwise remain until a record is deleted where the application allows it, or until an admin deletes the whole workspace. Record protection is enforced by the application and the database, not by write-once storage.
- No formal incident-response commitment. There is no contractual notification window today. If we learn of a breach affecting your workspace, we will contact the workspace admins directly; we are not going to dress that up as a certified process.
- Rate limiting is application-instance local. Sensitive endpoints are throttled, but the current limiter is not a distributed edge control. A larger or higher-risk deployment should add platform-level abuse protection.
If any of these is a blocker for your organization, say so. Knowing which gap actually stops a team from adopting the product is more useful to us than a guess.
Responsible disclosure
If you find a vulnerability, please email hello@bakaroart.com with enough detail to reproduce it — the affected URL or endpoint, the steps you took, and what you observed.
Please do not open a public issue for a security report. Give us a reasonable window to fix the problem before disclosing it publicly. We do not currently run a paid bug bounty, and we will not pretend otherwise, but we will acknowledge your report, keep you updated on the fix, and credit you if you would like to be credited.
We ask that you avoid testing that degrades service for other users, and that you do not access, modify or retain data belonging to an organization that is not yours. If you access someone else's data accidentally while investigating, stop, tell us, and delete it.
Why it is built this way
Boring controls, honestly described
Any team responsible for sensitive program data should be able to account for the tools it uses. The three ideas below are what we would want to defend in that conversation.
Enforced, not documented
Every control on this page is implemented in the application, and the ones that matter most — tenant isolation, role checks — are covered by automated tests that run before anything ships.
No third-party browser traffic
No analytics, trackers, external font or script host run in the browser. Server-side hosting, database, billing and optional email providers are disclosed above rather than hidden behind that browser claim.
Gaps stated up front
No certification, no third-party pen test, no single sign-on yet. You should hear that from us at evaluation time, not from a security questionnaire three months in.
Keep reading
Related pages
Privacy policy
What we collect, why we collect it, how long we keep it, and how to export or delete it yourself.
Read the privacy policyFeatures
Award records, subrecipient monitoring, published updates, the audit pack and the audit log the controls above protect.
See what is in the productAccess and pricing
Compare the per-organization plans by award count, and see how the trial, billing, renewal and cancellation work.
Compare plansQuestions your security review needs answered?
Send the questionnaire, the specific control, or the constraint you are working under. We will answer directly, including where the answer is "not yet".