Foundations · For CTOs, founding engineers, security architects
How to build a secure architecture from day one
The decisions that make a new system defensible before its first incident: five facts to write down, a skeleton with its boundaries drawn in, and defaults that hold without heroics.
5 min read7 sources
Most breaches walk through the structure of a system, not past its cleverest code. Day one is when that structure is cheapest to get right.
Security is a property of the structure
Look at how serious incidents unfold and a pattern repeats. An attacker rarely needs a novel exploit. They use an endpoint that was reachable when nobody meant it to be, a credential that could do far more than its job, a network that trusted anything inside it, or a pipeline that could change production without review. Those are properties of the architecture, not of any one line of code.
OWASP gave this its own category, Insecure Design, to separate it from implementation bugs: a flawless implementation of a design that lacks a control is still insecure. The fix is not another scanner in the pipeline. It is deciding, early and explicitly, where trust changes and what guards each change.
Day one is the cheapest moment to do that. The system is small enough to hold in your head, nothing depends on its accidents yet, and every default you set will be copied by the next ten services.
Write down five facts before the first service
Before choosing frameworks, write a one-page security brief. It answers five questions, and every later design review starts from it.
- What are we protecting? Name the data classes (customer records, payment references, credentials, model prompts, audit logs) and say which ones would hurt most to lose, leak or corrupt.
- Who acts on the system? List every kind of identity: customers, staff, partner systems, services, background jobs, the CI pipeline, operators with emergency access. Machines count.
- Where does trust change? Mark each point where a request crosses from one owner, network, privilege level or data sensitivity to another. These are your trust boundaries.
- How does change reach production? Describe the path from a merged commit to running code, and every credential on that path.
- What happens when one part fails or is taken over? Decide what a compromised component should not be able to reach.
The brief is deliberately short. Its value is that it forces the questions a threat model asks, in language a new engineer can follow, before the answers are fixed in infrastructure.
Draw the skeleton with its boundaries in place
A sensible starting point for most products is a modular monolith or a small set of services behind one edge, a private data tier and a delivery path that is treated as part of the system rather than as tooling around it.
Notice what the picture leaves out as much as what it shows. Nothing in the Internet zone reaches the database. Engineers never deploy from a laptop; they merge, and the pipeline deploys with a credential that expires in minutes. The application holds no database password in its code or its image: it reads what it needs at start-up from a secrets manager, using its own workload identity.
Read the diagram as text
- Internet: Customers, Engineers
- Edge: Edge gateway
- Delivery: CI/CD pipeline
- Private network: Application, Database, Secrets manager
- Customers → Edge gateway: HTTPS
- Edge gateway → Application: verified identity
- Application → Database: scoped role
- Engineers → CI/CD pipeline: reviewed merge
- CI/CD pipeline → Application: short-lived deploy
- Application → Secrets manager: read at start
Choose defaults that hold without heroics
Good defaults are the ones nobody has to remember. Set these before the first feature ships, because changing them later means touching every service.
- Deny by default. New endpoints, roles, network rules and storage buckets start closed. Opening one is a reviewed change with a reason.
- One identity per workload, none shared. Humans sign in through single sign-on with phishing-resistant multi-factor authentication. Services and jobs use workload identities issued by the platform, not long-lived access keys.
- Authorise in one place, on every call. Put access decisions in a shared library or policy service and call it from every handler. Authentication at the edge says who; only the service knows whether this caller may touch this record.
- Encrypt with managed keys. Use the platform's TLS termination and storage encryption, and keep key administration separate from data access.
- Production in its own account or project. A separate administrative domain is the cheapest blast-radius control you will ever buy.
- Log security events from the start. Sign-ins, permission changes, token creation and data exports, with enough retention to investigate an incident you discover months later.
- Pin and update dependencies on a schedule. Lock files, a small set of approved registries and an automated update cadence. See supply-chain boundaries.
- Give coding agents written rules. If agents write code in your repositories, tell them from the start how to handle secrets, which dependencies they may add and which checks they must never skip. Veriom's free Security.md tool generates security rules for coding agents, as SKILL.md, AGENTS.md or editor rule files, for the use case and target you choose.
- Publish how to report a vulnerability. A disclosure policy, and a
SECURITY.mdin each repository that says how to report a problem, cost an afternoon. CISA's Secure by Design pledge lists a vulnerability disclosure policy among its goals for exactly this reason.
Keep the architecture reviewable as it grows
The skeleton will not survive contact with the roadmap, and it should not have to. What has to survive is your ability to see the current structure and to notice when a change alters it.
Three habits make that possible. Keep infrastructure in code, so the network, identities and data stores are reviewable text rather than console state. Record significant decisions, including the security trade-offs, in architecture decision records. And define the changes that trigger a closer look: a new entry point, a new kind of identity, a new flow of sensitive data, a new third-party dependency with network access, or a new path to production.
A diagram drawn once at kickoff and never updated is worse than no diagram, because people trust it. Prefer a picture reconstructed from what is actually deployed and compared with what you intended.
What to postpone, on purpose
Securing a system from day one does not mean building everything on day one. Some choices are better deferred until the system tells you it needs them:
- Splitting into many services before the domain boundaries are clear. A modular monolith with enforced module boundaries is easier to secure than a distributed system with unclear ones.
- A service mesh, custom identity provider or bespoke cryptography. Use the managed equivalents until you have a concrete requirement they cannot meet.
- Exhaustive threat models of features that do not exist yet. Model the skeleton now, and each significant change as it lands.
Write the postponed decisions down with the condition that would reopen them. "We will add per-tenant encryption keys when the first enterprise customer requires it" is a decision. Silence is not.
A day-one checklist
- A one-page security brief answering the five questions above.
- A diagram of the skeleton with every trust boundary and the control on each crossing.
- Separate production account or project, with administrative access through SSO only.
- Workload identities for every service and job, and no long-lived keys in code, images or CI variables.
- Authorisation checked in the service on every request, through one shared mechanism.
- Private data stores reachable only from the workloads that need them.
- A deployment path where the only way to production is a reviewed merge.
- Security event logging with a retention period you have chosen deliberately.
- Pinned dependencies with a scheduled update process.
- A published vulnerability disclosure policy, and a
SECURITY.mdin each public repository that says how to report a problem.
Sources
- Secure by Design
CISA
- Secure Software Development Framework (SP 800-218)
NIST
- Engineering Trustworthy Secure Systems (SP 800-160 Vol. 1 Rev. 1)
NIST
- A04:2021 Insecure Design
OWASP
- Secure Product Design Cheat Sheet
OWASP
- The Protection of Information in Computer Systems
Saltzer and Schroeder, 1975
- Security pillar, AWS Well-Architected Framework
Amazon Web Services