Security — FieldPE
FieldPE holds a general contractor's drawings, specifications, correspondence and change-order money, and it holds an OAuth token that can write to their Procore. This page says what protects that, in enough detail to be checked.
Every control described here is enforced in code and covered by a test. Where something is not built, this page says so rather than leaving the gap for you to discover.
1. Nothing files without a person
FieldPE drafts. A person issues.
An RFI, a submittal review, a change order and a scope of work all land in Procore as drafts, or stay in a review queue, until someone at your company approves them. Nothing is sent to an architect, an owner or a subcontractor on its own. This is the product's central promise and the first thing to check if you are evaluating the rest of this page — if it were not true, none of the other controls would matter much.
2. Two separate boundaries around your data
Your company. Every tenant table is protected by PostgreSQL Row Level Security, and by FORCE ROW LEVEL SECURITY rather than plain ENABLE — the difference matters, because plain ENABLE exempts the role that owns the table, which is often the application's own role. A query that forgets its tenant filter returns no rows instead of everyone's.
Your job. Company-wide access is not the same as access to every project. What you can see in FieldPE is the company's activated projects *intersected* with what your own account can see in Procore. If your employer gave you three jobs, FieldPE does not widen that to the portfolio. This is applied in the request middleware and again in each local store, so a route written next week inherits it.
3. Signing in
Sign-in is Google or Microsoft OAuth. FieldPE never sees or stores your password.
Whether a session is required is set explicitly by configuration, not inferred from whether a sign-in provider happens to be configured. That distinction is deliberate: inferring it means a credential that goes missing turns authentication *off* rather than breaking it, and an application that quietly stops asking for sign-in looks exactly like one that is working.
4. Credentials at rest
Procore tokens, mailbox tokens and connected-account credentials are encrypted before they are stored, using Fernet (AES-128-CBC with HMAC-SHA256 authentication) under a key held only in the environment, never in the database or the repository.
Every secret-bearing configuration field is marked so it cannot be printed. A stack trace that touches configuration renders no API key, client secret, refresh token or session secret.
5. The browser
- Content Security Policy with
strict-dynamicand a nonce generated fresh for every response. No inline event handlers anywhere in the console. - Every value interpolated into HTML is escaped, enforced by a test that walks each sink to the end of its statement and checks every interpolation. The small number of expressions that go in raw are pinned by name, so adding one requires saying why.
Strict-Transport-Security,X-Content-Type-Options: nosniff,X-Frame-Options: DENY,Referrer-Policy,Permissions-Policy— on every response, including the redirects and error pages that short-circuit before a route runs.- Authenticated pages are
no-store. Public pages areprivate, so no shared cache holds a copy.
6. What we fetch, and where we send things
Attachments, drawings and upload destinations arrive as URLs inside API and webhook responses — which makes them input, not configuration.
Every such URL is resolved before it is fetched and refused if it points at a private, loopback, link-local or reserved address. Redirects are followed by hand and re-checked at each hop, because a check performed before a followed redirect says nothing about where the third hop lands.
Credentials are scoped by host, not by substring. api.procore.com.example.net is not Procore, and a signed storage URL is handed no token at all.
7. Requests from outside
- Inbound mail webhooks are verified against the provider's signature over the exact received bytes, with a five-minute timestamp window so a captured request cannot be replayed later.
- Endpoints for providers we have not actually integrated are not served. A route that accepts traffic from strangers using a scheme its named provider does not use is worse than no route.
- Rate limits are applied outside the authentication gate, so an unauthenticated flood is refused rather than counted after the fact, and are keyed on the real client address as our edge reports it.
- Request bodies are bounded by counting bytes as they arrive, not by trusting the declared length.
8. Audit
Actions that matter are written to an append-only log. Append-only is enforced by the database: the application's role holds INSERT and SELECT and has had UPDATE and DELETE revoked. An actor cannot erase their own entry, which is the only property that makes an audit log worth keeping.
9. What we do not claim
We would rather you learn this here than assume it.
- No SOC 2 report and no ISO 27001 certificate. Neither audit has been performed. If you need one to buy, tell us and we will talk about timing honestly rather than pointing at a badge.
- No third-party penetration test to date. The controls above are enforced by tests and verified against the running service; they have not been attacked by an outside firm.
- No bug bounty programme. Disclosure is handled by email, below.
- Sub-processors and data handling are covered by the Privacy Policy, which is the authoritative document for what data is held, where, and for how long. This page describes controls, not scope.
10. Reporting something
Email hello@capstones.ai with "security" in the subject.
Tell us what you found and how to reproduce it. We will confirm receipt, and we will tell you what we did about it. Please do not access, modify or retain another customer's data while demonstrating an issue — describe the path and we will reproduce it ourselves.
FieldPE is operated by Kashi LLC.