Overview
Radish is a platform for designing, generating, and operating applications from structured blueprints.
Instead of producing throwaway prototypes or opaque AI-generated code, Radish compiles explicit application descriptions into maintainable architecture. The platform is designed to help developers move quickly from an application idea to a real system foundation that can be inspected, versioned, extended, and deployed.
Radish is built around a simple flow:
Intent → Blueprint → Architecture → Application
A user or team describes an application. That intent is translated into one or more structured blueprints. Radish compiles those blueprints into architecture layers. Developers and AI tools then build application behavior and presentation on top of that generated foundation.
The recommended entry point for new projects is radish init, which bootstraps a project directory with a blueprints/ folder, Claude Code skills, and a CLAUDE.md. From there, developers can use the generated /init slash command to create blueprints conversationally, then run radish create app to generate the full stack.
Core Components
| Component | Purpose |
|---|---|
| Radish CLI | Compiles blueprints into generated architecture |
| Radish Hub | Orchestrates applications, blueprints, and deployments |
| Radish Cloud | Hosted management for hubs, tenants, and environments |
| Radish Lib | Library of blueprint patterns, prompts, and reusable designs |
What Radish Generates
Radish generates a complete, production-ready application stack. Some infrastructure is always-on — present in every generated app with no flags to set. Other capabilities are feature-gated — enabled per-project in the app blueprint when you need them, absent when you don't. This keeps simple apps simple while giving complex apps the infrastructure they need without writing it from scratch.
Multi-Stack Generation
Because blueprints describe what an application is — not how it's built — the same blueprint can compile to different technology stacks. Radish is opinionated about architecture and process but agnostic about language and framework.
| Stack | Language | Framework | Database | Status |
|---|---|---|---|---|
| Node (default) | TypeScript | SvelteKit | MongoDB | Production |
| Laravel | PHP | Laravel | MySQL/PostgreSQL | Production |
| Python | Python | — | — | In development |
Database adapters are also expanding: MongoDB is the default, with SQLite and localStorage adapters in progress for desktop and lightweight deployments.
The generation flag is a single option — radish create datalayer --stack laravel — and the same blueprints, roles, and features compile to the target stack's idioms: Eloquent models instead of Mongoose, Form Requests instead of Zod schemas, Sanctum instead of cookie sessions, Policies instead of service-layer guards.
Datalayer
- Contracts and validation (Zod schemas / Form Requests)
- Repositories and persistence access (MongoDB/Mongoose, Eloquent)
- Services with four-layer access control (see below)
- Field metadata APIs for runtime introspection
- REST API routes (SvelteKit / Laravel controllers)
- Generated documentation (API docs, OpenAPI spec)
Four-Layer Access Control
Every service method is gated by a layered authorization model. The layers compose — each narrows the result set from the previous one:
- RBAC — role-based permission check. Roles are defined in the roles blueprint and seeded at startup. Each role holds a set of permissions (
entity:action), and the service rejects the call if the user's role lacks the required permission. - Ownership — record-level filtering. Entities with
"owned": trueautomatically scope queries toownerId === currentUserfor non-admin roles. Admins see everything; regular users see only their own records. - Scoped access — tenant or organizational boundaries. Multi-tenant apps filter by
tenantIdso users in one tenant never see another tenant's data, even if they share the same role. - Grants — per-record ACL (feature-gated:
features.grants). Fine-grained sharing: "User A grants User B read access to this specific Order." Grants extend visibility beyond ownership without elevating the user's role.
This model is generated, not hand-wired. Define an entity's ownership mode and role permissions in the blueprints, and every service method, API route, and admin page respects the rules.
Application Scaffolding
- SvelteKit skeleton with route groups ((auth), (public), (app))
- Admin panel with entity CRUD, user management, role management
- Setup wizard at
/setupfor first-time admin creation - SEO infrastructure (sitemap.xml, robots.txt, llms.txt)
- Login/logout with signed session cookies and API key support
- Password recovery via
PasswordResetToken(always-on builtin entity) - Theme layer generation (DaisyUI theme from blueprint)
Blueprint Verification & Versioning
Blueprints are the source of truth. Radish tracks the relationship between blueprints and generated code:
- Hash tracking — every generated file records which blueprint version produced it
- Drift detection —
radish verifycompares current blueprint hashes against what was last generated, surfacing when code is stale - Orphan detection — finds generated files that no longer correspond to any blueprint entity (e.g., after removing an entity)
- Doctor —
radish doctorruns the full suite: blueprint validation, drift check, orphan scan, search index status - Convert —
radish converthandles YAML ↔ JSON for teams migrating legacy blueprints - Migrate —
radish migratemanages live schema migrations:generatediffs the current blueprint against the last snapshot and produces a migration file,statusshows pending migrations,upapplies them to MongoDB, anddownrolls back the last applied migration
This means a team can answer "is this app in sync with its blueprints?" at any time, and CI can enforce it.
Always-On Infrastructure
These subsystems are generated in every app — no feature flags, no configuration. They represent the baseline that every Radish app ships with.
Observability
- Correlation IDs on every request (generated, propagated through service calls)
- Service instrumentation — every CRUD method tracked with timing and outcome
trackedFetch()for external API calls (Dynamics 365, Stripe, etc.) with automatic correlation- Structured logging with correlation-aware logger
- Health endpoint with real-time metrics
- Hub reporter for centralized monitoring across apps
Rate Limiting
- Default tiers: auth endpoints (20/min), API routes (100/min), public routes (60/min)
rateLimit()— apply a named tier to any routeregisterTier()— define custom rate limit tierswithRateLimit()— wrap custom endpoints with per-route limits- Automatic
429 Too Many RequestswithRetry-Afterheader
Idempotency
- Every mutating API call supports an
Idempotency-Keyheader - Duplicate requests within the TTL return the original response without re-executing
- Stored as
IdempotencyKeyentities with automatic expiry - Prevents double-creates from network retries, webhook replays, and client bugs
Scheduled Jobs
- Handler registry —
registerJobHandler()with cron or interval scheduling - Timezone-aware cron evaluation and configurable timeouts
- Full execution lifecycle: running → completed / failed / cancelled
- Consecutive failure tracking with automatic reset on success
- Manual trigger from admin panel (
triggeredBy: 'manual') - Job Snapshots — declared metric shapes on handlers (dimensions, metrics, bucket grain)
- Validated
context.snapshot()API with in-memory buffering - Bucketed upsert for idempotency (re-runs overwrite, not duplicate)
series()androllup()query methods with weighted-average support- Cardinality cap to prevent unbounded growth
- Validated
Entity Versioning & Audit Trails
Any entity can opt into change tracking by setting versioning in the types blueprint — no feature flag, no extra wiring. Radish generates the models, contracts, services, API endpoints, and admin UI for whichever strategy you choose:
- Full versioning (
versioning: "full") — complete entity snapshots on every change, version comparison (/versions/compare?a=3&b=5), and revert to any prior version (POST /revert/{version}) - Simple versioning (
versioning: "simple") — lightweight audit logs storing only field-level diffs (old/new values), 10–20× less storage than full snapshots - Field-level tracking (
trackChanges: trueon individual fields) — enhanced change metadata with approval workflows (pending → approved/rejected) for sensitive fields like pricing or contract terms - Strategies compose — an entity can use full versioning for compliance while also tracking critical fields with approval gates
- All versioning respects the access control model — users need
entity:viewto see history, and change records always capturechangedBy
Field-Level Encryption
Sensitive data is encrypted at rest using AES-256-CBC, with two complementary mechanisms depending on where the secret lives:
- Entity fields — declare any field as
"type": "encryptedKey"in the types blueprint. The value is encrypted before writing to the database and decrypted on read through the service layer. API responses return[ENCRYPTED]— the raw ciphertext never leaves the server. - Settings with
fieldSchema— the Settings entity stores complex objects (API credentials, integration configs) where only specific fields need encryption. Declare afieldSchemamarking which fields areencryptedKey, and the rest stay readable for debugging while secrets are encrypted:{ "serverUrl": "string", "clientId": "string", "clientSecret": "encryptedKey" } - Single encryption key — both mechanisms use the same
RADISH_ENCRYPTION_KEYenvironment variable. Missing key + encrypted field access produces a clear error with generation instructions, not a silent failure. - Scoped settings — Settings support a three-tier cascade (system → tenant → user) with encryption working at all levels, so tenant-specific API keys stay isolated and encrypted.
- API masking —
list()and REST responses mask encrypted fields as[ENCRYPTED]. Server-side code usesget()/getDecrypted()for plaintext access.
Feature-Gated Capabilities
These are enabled per-project in the app blueprint (features: { search: true, cms: true, ... }). When disabled, zero code is generated for that feature — no dead imports, no unused models, no wasted surface area.
Search (features.search)
- Typesense integration for full-text search, filtering, faceting
- Index schemas with field type mapping and
toSearchDoc()mappers - Index filters for membership control (match → upsert, no match → delete)
- Search feedback entities (SearchQuery, SearchClick, SearchAssociation)
- Inline and background sync modes
- Search API endpoint (
/api/v1/search) - CLI tools:
radish search reindex,radish search status,radish search doctor
Grants (features.grants)
- Per-record ACL — share individual records with specific users
- Grant entity with
targetType,targetId,granteeId,permissions - Integrates with the access control layer — granted records appear alongside owned records in list queries
- Service-level
$orfiltering:{ ownerId } OR { _id: { $in: grantedIds } } - Revocation and expiry support
CMS (features.cms)
- ContentBlock — rich content with versioning, status lifecycle (draft → published → archived)
- MediaAsset — file metadata with type classification and alt text
- Attribute — key-value metadata attachable to any entity
- FormTemplate — reusable form definitions with field schemas
- Rule — conditional logic definitions for workflows and validation
Datasets (features.datasets)
- Analytics aggregation engine for metrics, KPIs, and reporting
- DatasetDefinition — declares a dataset: source, grain columns, sync schedule, indexed dimensions
- DatasetField — column definitions with aggregation type (sum, avg, count, min, max)
- DatasetRow — time-series data points with dimension/metric pairs
- Sync engine — source-agnostic, maps external data through column definitions into dataset rows
- Rollup, trend, and topN query methods
- Compound key upsert for idempotent sync (re-sync overwrites, never duplicates)
AI Context & Skills
Radish generates a complete AI development environment alongside the application code. The goal is that an AI agent dropped into a generated project can immediately understand the architecture, follow the framework's conventions, and extend the application without breaking generated boundaries.
- Slash commands —
/apiinjects the full REST API reference (endpoints, auth, filters, response shapes);/radishre-injects project context (entities, fields, roles, features, directory ownership);/blueprintprovides blueprint authoring context;/searchprovides search infrastructure context (generated conditionally when search-enabled entities exist) - Agent definitions — specialized agents (orchestrator, datalayer, roles, ui, admin) with focused file access boundaries and delegation rules
- Skills — task-oriented procedures that agents invoke to perform common operations correctly:
add-entity— walk through adding a new entity to the blueprint, validate, regenerateadd-feature— enable and configure features (search, CMS, grants, datasets)diagnose— run health checks across blueprints, generation status, and search indexesinit— guided blueprint generation for a new projectstatus— inspect current project state (entities, roles, features, generation drift)
- Reference docs — the platform docs at
.radish/docs/and the app's generated API reference at.radish/lib/datalayer/docs/, both available to agents via@imports - All context is derived from blueprints — entity-accurate, always current, regenerated alongside the application code
Deployment Artifacts
- Dockerfile (Debian slim, multi-stage build, healthcheck)
- docker-compose.yml (production-ready)
- .gitlab-ci.yml (Kaniko build, deploy trigger)
radish deploy initfor Coolify provisioning
Design Priorities
Radish is built to support:
- deterministic generation — same blueprints always produce the same code
- framework agnosticism — blueprints describe what, not how; the same definition compiles to Node/SvelteKit, Laravel/PHP, or future stacks
- inspectable output — generated code is readable, not minified or obfuscated
- blueprint versioning — track, diff, and audit changes to your application definition
- AI-assisted workflows — generated context and skills keep AI agents aligned with the framework
- feature gating — opt into complexity only when you need it
- portability across repositories, packages, and deployment models
- gradual evolution toward a broader application compiler