Design System.
A self-hosted brand and design-asset library: a Next.js admin UI and a standalone Express REST API serving brand identities, design tokens, component specifications, page templates, and thousands of icons — to humans and to AI agents through MCP.
Install
Executed from a clean container against this repository:
git clone https://github.com/bulletproofsoftware-ai/bulletproof-design.git cd bulletproof-design npm ci --legacy-peer-deps # legacy peer deps: eslint 9 vs plugin ranges

bulletproof-design/docs/media/1Problem Statement
Brand assets, design tokens, component specifications, and icon sets typically live in different tools — a design file, a token repository, a component library, a shared drive. Nothing is queryable from one place, and the canonical value of a brand colour depends on who you ask.
AI agents make this worse. An agent asked to build a page in a company's style has no way to look up that style, so it guesses: approximate colours, invented spacing, components that do not exist. The output looks plausible and is subtly wrong in every dimension the brand actually specifies.
This system stores brands, tokens, components, templates, and icons as files on disk and serves them through four coordinated surfaces — an admin UI for editors, a public portal for stakeholders, a REST API for applications, and MCP tools for agents — so every consumer reads the same source of truth.
2Architecture
Two processes run under supervisord in a single container: the Next.js application on port 8095 and a standalone Express API on 8096. Both read the same on-disk registries, so neither is authoritative over the other.
Resource routers
src/api/server.ts mounts a router per resource — categories, templates, components, search, brands, import, assets, icons — plus a preview route and a /brand-assets static route deliberately mounted outside /api so CORS and auth gates do not apply to public assets.
Method-scoped auth
Writes (POST, PUT, DELETE, PATCH) require X-Api-Key checked against DESIGN_API_KEY through a constant-time safeCompare(). Reads pass through unauthenticated. Rate limits are 100 writes and 1000 reads per 15 minutes.
Environment-split CSP
middleware.ts emits a per-request 128-bit nonce with 'strict-dynamic' in production and no 'unsafe-inline' or 'unsafe-eval' on script-src; development retains both so HMR and the Monaco editor work.
Enforced component tiers
A four-tier architecture is enforced mechanically in eslint.config.mjs via no-restricted-imports: ui/ cannot import primitives, features, or effects; primitives/ cannot import features or effects; features/ cannot import effects or reach across features.
MCP server
src/mcp/design-system-server.ts registers ten tools against the same registries, letting an agent fetch a component, search icons, read brand guidelines, or retrieve design tokens directly.
Pipeline and Safety
- Token compilation — DTCG tokens plus dark and high-contrast themes compile to CSS custom properties through Style Dictionary, run automatically by
predevandprebuildso tokens can never drift from the compiled output. - SVG sanitization —
svgSanitizer.tsruns on both upload and icon sync, aborting on<script>elements,on*handlers, or external entity references. - Path-traversal guard —
safeJoin.tsconstrains every filesystem read to its intended directory. - Directory-based brands — each brand is a directory containing
brand.json,guidelines.md, and an assets folder, with a migration script from the older flat-JSON format. - Virtualized icon browsing — roughly 2,500 icons in each of three styles (about 7,500 SVGs in total) render through
@tanstack/react-virtualrather than mounting thousands of nodes. - Live playground — Monaco plus
@babel/standalonecompiles component examples in the browser for immediate preview.
3Requirements
Requirements describe the implemented capabilities across the four surfaces.
| ID | Requirement |
|---|---|
| REQ-DS-001 | Serve brands, tokens, components, templates, and icons from on-disk registries. |
| REQ-DS-002 | Expose an admin UI for editing brands and browsing component specifications. |
| REQ-DS-003 | Expose a public per-brand portal covering colour, typography, logo usage, and imagery. |
| REQ-DS-004 | Expose a REST API documented by an OpenAPI specification. |
| REQ-DS-005 | Expose ten MCP tools so agents can query the system directly. |
| REQ-DS-006 | Require an API key for all mutating requests and rate-limit reads and writes separately. |
| REQ-DS-007 | Emit a strict nonce-based Content Security Policy in production. |
| REQ-DS-008 | Enforce the four-tier component import hierarchy at lint time. |
| REQ-DS-009 | Compile design tokens and themes to CSS custom properties on every build. |
| REQ-DS-010 | Sanitize all SVG content on upload and on sync. |
| REQ-DS-011 | Extract component props from TSX sources for the specification browser. |
| REQ-DS-012 | Default the public portal to noindex unless explicitly enabled. |
4Interfaces
Four surfaces over one store. The MCP tool set is what makes the system legible to agents.
| Interface | Purpose |
|---|---|
| GET /api/brands · /api/brands/{slug} | Brand CRUD and retrieval |
| GET /api/brands/{slug}/colors · /fonts · /css-variables | Brand token access |
| GET /api/components · /api/search | Component registry and search |
| GET /api/templates/{category}/{name} | Page template retrieval |
| GET /api/icons/search | Icon search across styles |
| GET /preview/{category}/{name} | Rendered HTML preview |
| get_component · get_component_spec | MCP: component and prop specification |
| get_brand_identity · get_brand_guidelines | MCP: brand identity and written guidelines |
| get_tokens · get_composition_rules | MCP: design tokens and composition rules |
| search_icons · get_icon · get_asset · search_components | MCP: asset and component discovery |
5Integration Points
The system is designed to be consumed by other software as much as by people.
- MCP clients —
.mcp.jsonruns the design-system server so any MCP-capable agent can query brands and components. - GitHub API — icon synchronisation pulls Material Symbols from the upstream icon repository.
- Anthropic SDK — template generation, with an optional Gemini key as an alternative.
- Docker and supervisord — both processes run in one container with separate exposed ports.
- Syft — CycloneDX SBOM generation, alongside OpenSSF Scorecard and Dependabot workflows.
6Repository
The repository separates the admin application, the API, the MCP server, and the content registries.
| Path | Purpose |
|---|---|
| app/(admin)/ | Admin editing interface |
| app/portal/[slug]/ | Public per-brand portal |
| src/api/ | Express routers, middleware, and libraries |
| src/mcp/design-system-server.ts | MCP server exposing ten tools |
| components/{ui,primitives,features,effects} | The four enforced component tiers |
| design-tokens/ | DTCG tokens, themes, and Style Dictionary config |
| templates/ | Thirteen full-page templates |
| brands/ | Directory-based brand definitions |
| docs/ | Architecture, API, deployment, migration, SBOM, and review documents |
7Implementation Notes
Configuration defaults and constraints that affect deployment.
- The public portal is secure by default: it emits
noindex, nofollowunlessPORTAL_INDEXis explicitly enabled, and staging environments should leave it unset. DESIGN_API_KEYis required in production — without it, write protection is not meaningful.DISABLE_ICONSandDISABLE_ENRICHED_COMPONENTSaffect router mounting at startup and therefore require a restart.- The dependency audit fails the build on any high or critical advisory and runs inside the Docker build.
- SBOM generation requires
syftto be installed on the build host. - The icon SVGs are not committed — the repository ships a category map and metadata schema, and the icon set must be synced from GitHub before the icon browser is populated.