Project profiles describe how much assurance a target application needs. AIDD reads them from:
.aidd/project-profile.json
When this file exists and validates, AIDD treats the profile as explicit. When it is missing or
invalid, readProjectAssuranceProfile() falls back to inference from project signals such as
package.json, stack metadata, dependencies, and archive-style paths.
{
"authMode": "rbac",
"bucket": "multi_user_local",
"criticality": "utility",
"dataSensitivity": "personal",
"deployment": "local",
"externalIntegrations": "none",
"notes": "Optional context for why this profile was chosen.",
"source": "explicit",
"updatedAt": "2026-05-19T00:00:00.000Z"
}The web project Profile UI writes this canonical shape. source is always explicit for the file
contract, and updatedAt is an ISO timestamp from the write operation. Files without source or
updatedAt are invalid and are treated the same as a missing explicit profile.
notes is optional. It is trimmed, capped at 4000 characters, and omitted when empty.
All six core fields are required enums. Within each field, values are listed from lowest to highest
assurance — the same order they appear in the Profile UI dropdowns. The UI label column matches
the short text shown in the web Profile tab and project list badges. Values marked ⊕ full
hardening satisfy a clause of requiresFullHardening(); values marked ⊖ low-exposure are the
only ones permitted by isLowExposureLocalProfile() (see Operational Effect).
| Value | UI label | Meaning |
|---|---|---|
prototype_archive |
Archive | Throwaway, retired, or snapshot code not run in anger. Inferred automatically for .old directories and /archive paths. |
single_user_local |
Single-user local | One person runs it on their own machine; no other users. ⊖ low-exposure. Inferred default for non-Spernakit local tools. |
multi_user_local |
Multi-user local | Multiple users, but still on a local/trusted network (e.g. a household or workshop). Inferred default for full Spernakit apps. |
private_team |
Private team | Used by a defined internal team, typically behind a private server and not exposed to the public internet. |
internet_single_org |
Internet org | Internet-reachable but serving a single organization's users. Inferred for react+convex / cloud stacks. |
public_multi_tenant |
Multi-tenant | Public SaaS serving many independent tenants or customers. ⊕ full hardening. |
critical_regulated |
Critical | Mission-critical and/or operating under regulatory obligations. Highest assurance tier. ⊕ full hardening. |
| Value | UI label | Meaning |
|---|---|---|
none |
None | No meaningful data to protect. ⊖ low-exposure. |
low |
Low | Trivial or non-identifying data; leakage is inconsequential. ⊖ low-exposure. |
personal |
Personal | Personal data tied to individuals (PII) — names, emails, personal notes. |
confidential |
Confidential | Sensitive business or secret data whose disclosure would cause real harm. |
regulated |
Regulated | Data under a legal/compliance regime (health, financial, etc.). ⊕ full hardening. |
| Value | UI label | Meaning |
|---|---|---|
local |
Local | Runs only on the developer/owner's own machine. ⊖ low-exposure. |
lan |
LAN | Runs on a local/private network (home or office LAN); not internet-exposed. |
private_server |
Private server | Hosted on a server but restricted to a private/internal audience. |
public_server |
Public server | Hosted on an internet-reachable server. ⊕ full hardening. |
cloud |
Cloud | Runs on managed/public cloud infrastructure. ⊕ full hardening. |
| Value | UI label | Meaning |
|---|---|---|
none |
None | No authentication; anyone with access can use the app. |
local_owner |
Local owner | A single implicit owner (the local OS user); no login screen — identity is the machine user. |
login |
Login | Authenticated users sign in, but all authenticated users are equal (no role distinctions). |
rbac |
RBAC | Role-based access control; permissions vary by assigned role. |
tenant_rbac |
Tenant RBAC | Role-based access scoped per tenant — combines tenant isolation with roles. |
| Value | UI label | Meaning |
|---|---|---|
toy |
Toy | Experiment or demo; failure has no consequence. ⊖ low-exposure. |
utility |
Utility | Helpful tool; failure is an easily worked-around inconvenience. ⊖ low-exposure. |
operational |
Operational | Supports day-to-day operations; failure disrupts real work. |
business_critical |
Business critical | Core to the business; failure causes significant harm. ⊕ full hardening. |
| Value | UI label | Meaning |
|---|---|---|
none |
None | No external/outbound integrations. ⊖ low-exposure. |
read_only |
Read-only | Reads from external systems but cannot modify them. ⊖ low-exposure. |
write_capable |
Write-capable | Can write to or modify external systems. |
financial_or_security |
Financial/security | Integrates with financial or security-sensitive systems (payments, auth providers, secrets). ⊕ full hardening. |
When no valid explicit file is present, inferProjectAssuranceProfile() picks one of four preset
profiles from project signals (first match wins):
| Signal | bucket | authMode | criticality | dataSensitivity | deployment | externalIntegrations |
|---|---|---|---|---|---|---|
.old directory or /archive path |
prototype_archive |
none |
toy |
none |
local |
none |
convex dependency or react+convex stack |
internet_single_org |
login |
operational |
personal |
cloud |
write_capable |
Spernakit stack (excluding spernakit-lite) |
multi_user_local |
rbac |
utility |
personal |
local |
none |
| Fallback (anything else) | single_user_local |
local_owner |
utility |
low |
local |
none |
Inferred profiles carry source: "inferred"; writing through the Profile UI promotes the profile to
source: "explicit" and persists the canonical file.
The shared schema and runtime normalizer live in:
shared/src/contracts/project-profile.ts
This module exports:
projectAssuranceProfileInputSchemafor web/API write input.projectAssuranceProfileFileSchemafor canonical.aidd/project-profile.jsonfiles.normalizeProjectAssuranceProfileInput()for UI/API writes.normalizeProjectAssuranceProfileFile()for explicit file reads.
The CLI and backend use these shared helpers so enum values, notes handling, and canonical output
stay aligned. The backend PUT /api/v1/projects/:id/profile route builds its TypeBox request body
from the same shared enum arrays.
Profiles drive two distinct downstream behaviors:
- Audit applicability is governed by an external mapping file pair, not by code: the global
audits/audit-profile-mapping.jsonand per-project<project>/.aidd/audit-profile-overrides.jsontogether resolve each(profile, audit)pair to one ofdefault | disabled | required | excluded. The resolver is shared betweenfilterApplicableAuditNames, the director's audit discovery, and Stage 6 maturity. Seeaudit-applicability.mdfor the file format, precedence ladder, and matrix UI. - Risk-level posture is still expressed as code-side predicates against the profile shape:
requiresFullHardening(profile)(public, regulated, cloud-deployed, financially/security- integrated, or business-critical) andisLowExposureLocalProfile(profile)(small local tools). These are consumed bydirectorPriorityto bias suggestion severity; they describe profile semantics rather than audit selection and are defined inshared/src/contracts/audit-profile-mapping-resolve.ts(re-exported throughshared/src/metadata/project-profile.ts).requiresFullHardening(profile)returnstruewhen any of:bucketispublic_multi_tenantorcritical_regulated;dataSensitivityisregulated;deploymentispublic_serverorcloud;externalIntegrationsisfinancial_or_security; orcriticalityisbusiness_critical.isLowExposureLocalProfile(profile)returnstrueonly when all of:bucketissingle_user_local;deploymentislocal;dataSensitivityisnoneorlow;externalIntegrationsisnoneorread_only; andcriticalityistoyorutility.