Security at RootSpend
You are trusting us with keys to your bills. Here is exactly what we do with them, with the details to check it, not just promises.
- Read only
- Keys encrypted
- Data walled off
- AI on your terms
- Every change logged
- Yours to export or delete
We only read
RootSpend reads costs and usage. It never creates, changes or deletes anything in your tools. Here is every permission each connector asks for, word for word.
Billed by usage
- Read organization cost reports
- Read organization usage reports
- Read workspaces and API key names (never the keys themselves)
- Read organization members
- Read analytics (read:analytics): cost and usage reports, per user
- Read organization costs
- Read organization usage
- Read projects
- Read organization members
- SELECT on SNOWFLAKE.ORGANIZATION_USAGE.USAGE_IN_CURRENCY_DAILY, granted with GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE
- Read ACCOUNT_USAGE warehouse metering, query attribution, Cortex AI usage and users, to show cost per warehouse and person (same grant)
- USAGE on one small warehouse, to run the read only query
- BigQuery Data Viewer on the billing export dataset
- BigQuery Job User on the project the query runs in
Cloud bills
- ce:GetCostAndUsage (Cost Explorer, read only)
- sts:AssumeRole on the role you give, when you give one
- BigQuery Data Viewer on the billing export dataset
- BigQuery Job User on the project the query runs in
- Cost Management Reader on the subscription or billing account you give
Billed per seat
Your keys stay locked up
A key is encrypted before it touches the database, and only decrypted for the moment a sync needs it.
- 1You paste it and can test it with a read call before you save.
- 2It is encrypted with Fernet: AES 128 encryption, signed with HMAC SHA256. A tampered value refuses to decrypt.
- 3It is stored encrypted next to your tool, never in plain text.
- 4It is used in memory only while a sync runs, then thrown away.
- 5It never comes back No screen, API, export or log returns it. AI keys show a masked hint, nothing more.
- 6You stay in control Replace it or disconnect the tool at any time.
- In production, the server refuses to start without a valid encryption key, so it can never fall back to storing keys in plain text.
- Error messages from vendors are scrubbed of every credential before anything is logged.
- RootSpend only calls each vendor’s own addresses. For example, it won’t follow an Azure link to any host other than management.azure.com.
Your data is walled off, twice
Every request is tied to one company. The app filters by it, and the database enforces it again on its own.
Each table that holds company data has this rule, straight from our database setup. A request that doesn’t say which company it is for sees nothing at all, and a write for the wrong company is refused by the database, not just skipped by our code.
ALTER TABLE tools ENABLE ROW LEVEL SECURITY;
ALTER TABLE tools FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON tools
USING (org_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid)
WITH CHECK (org_id = NULLIF(current_setting('app.current_org_id', true), '')::uuid);- FORCE means the rule applies to RootSpend’s own database user too, not just to everyone else.
- Background jobs, like syncs and analyses, run inside the same rule, one company at a time.
The AI analyst, on your terms
It reads a short summary of your spend and suggests savings. It never acts, and it never gets the last word on a number.
What it sees
- Spend totals by tool, service, resource, category, team and project
- How each total changed against the period before
- How steady each cost is from day to day
- How many people have spend on a tool: a count, never who
What it never sees
- Names or emails of people
- Raw bill lines
- Your keys
- Your tables, files or prompts
- Notes your team writes, like why a finding was dismissed
{
"currency": "USD",
"period": { "start": "2026-09-28", "end": "2026-10-27", "days": 30 },
"totals": { "spend": 18158.42, "change_pct": 4.6 },
"tools": [
{ "id": "t1", "name": "Snowflake", "connector": "snowflake",
"category": "Databases", "spend": 7906.10, "change_pct": 2.4,
"people_with_spend": 14 }
],
"services": [
{ "id": "s1", "tool": "t1", "name": "ANALYTICS_WH", "spend": 3120.50,
"active_days": 30, "period_days": 30, "daily_variation_pct": 8.2 }
],
"teams": [ { "name": "Data", "spend": 3688.00, "change_pct": 6.1 } ],
"daily_spend": [ { "day": "2026-10-27", "spend": 612.40 } ]
}- Names from your tools are sent as data the AI is told never to follow, so text hidden in a resource name can’t give it orders.
- The AI never writes an amount. It writes placeholders, and RootSpend fills in every figure from your data.
- Each finding must cite the exact spend it is about. Anything that cites spend that doesn’t exist is dropped, and no saving can be bigger than what you really spend.
- Links and markup are stripped from its answer before anyone sees it.
- It can’t change anything in your tools. People decide, and RootSpend measures the result 14 days later.
- AI spend is capped. Each request reserves its worst case cost before it is sent, so an allowance can’t be overrun.
- An admin chooses RootSpend AI, your own Anthropic, OpenAI or Gemini key, or off.
Data about people is opt in
Seeing what each person costs is useful, and sensitive. So it is off until you decide otherwise.
- Costs per person stay off until an owner turns them on for the organization.
- Even then, only roles allowed to see people can view them. Everyone else sees teams and tools.
- People data is never sent to the AI analyst.
Accounts and sign in
The basics, done properly.
- Passwords are hashed with bcrypt. Nobody can read them, including us.
- Sign in, sign up and password reset allow 5 attempts a minute from one address, which stops password guessing.
- Your session lives in a secure, httpOnly cookie that scripts on the page can’t read. Signing out or changing your password ends it.
- In production the sign in cookie only travels over HTTPS.
- Roles decide who can view costs, manage budgets, connect tools or see people.
Our own access is limited, and you can see it
Sometimes we need to look at an organization to help. That access is narrow and leaves a trace.
- 1
On the list
Only emails on a list kept on the server can open the console. No button in the app adds anyone.
- 2
Second factor
A code from an authenticator app, which can’t be reused.
- 3
30 minutes
Console sessions end after 30 minutes, or when the person signs out.
- Inside your organization, staff can only look. Anything that would change your data is refused.
- Every visit is written to your own audit log, as platform_admin.viewed.
Every change is logged
Who did what, and when. A few of the events your audit log records:
tool.connectedtool.reconnectedtool.deletedbudget.updatedalert_rule.createdgroup.people_setmember.updatedinvitation.createdai.settings_updatedai.key_deletedrecommendation.marked_appliedorg.exportedplatform_admin.viewed
Keys are never written to the audit log, not even masked.
Your data, to export or delete
It is yours. You can take it with you, or remove it for good.
- Owners can export everything at any time: every table that holds your organization’s data.
- The export leaves out your connector keys, and drops any field that looks like a secret.
- Deleting an organization removes every one of those tables in one go, after you type its exact name.
This website
The site you are reading is as careful as the app.
- No cookies, no analytics and no advertising trackers.
- Served only over HTTPS, with strict security headers.
- The contact form is delivered by Web3Forms straight to our inbox. We don’t add you to any list.
Report a security issue
Found something? Tell us, and we will look at it straight away.
Email hello@rootspend.com with what you found and how to reproduce it.
Or use the contact formQuestions security teams ask us
No. RootSpend only reads costs and usage. Wherever a vendor offers a read only key, role or user, that is all it asks for, and it never creates, changes or deletes anything in your tools.
No. Keys are encrypted before they are stored and are only decrypted in memory while a sync runs. No screen, API, export or log ever shows them again, so not even our own team can read them back.
Only for support, only read only, and only after passing three checks: an email on a list kept on the server, a code from an authenticator app, and a session that ends after 30 minutes. Every visit is written to your own audit log.
RootSpend never trains models on your data. The AI analyst gets spend totals only, never people, raw bills or your keys. With RootSpend AI the request goes through the provider’s business API; with your own key, your own agreement with that provider applies. An admin can switch the AI off at any time.
An owner can export everything at any time, then delete the organization. The delete removes every table that holds your data in one go, and it asks for the organization’s exact name first.
Ask us anything about security
Book a demo and bring your security questions. We will walk through every one of them, on screen.
Opening soon. The waitlist gets in first.