The Backend ​
In Carotene, the backend block defines your server-side environment. It is the secure boundary where durable data is stored, heavy computation occurs, and external public traffic is routed.
Whether you are building a simple Node.js CRUD API or a high-performance Rust microservice architecture, the backend block provides the exact primitives you need, governed by strict compiler boundaries.
Monoliths vs. Microservices ​
Carotene uses namespace merging to determine your deployment architecture.
- The Monolith: If you use an unnamed
backendblock across multiple.carrotfiles, the compiler merges them all into a single, cohesive server deployment. - Microservices: If you name your backends (e.g.,
backend OrderService), the compiler isolates them. It generates independent deployments, distinct database schemas, and wires up the internal gRPC/HTTP service mesh automatically.
domain Commerce {
// Compiles into a standalone Go microservice
backend InventoryService {
config go { version: "1.21" }
function DeductStock(id: UUID) { ... }
}
// Compiles into a standalone Node microservice
backend OrderService {
config node { framework: "fastify" }
function PlaceOrder(itemId: UUID) {
// Magic Service Mesh: Safely calls across the microservice boundary
calls InventoryService.DeductStock
}
}
}Data & Memory ​
A backend typically manages both durable data (disk) and volatile session data (RAM). Carotene strictly separates these concepts to prevent data loss and optimize infrastructure generation.
store: Represents persistent disk storage. By default, Carotene infers a Postgres database and generates SQL migrations and ORM models.state: Represents high-speed, volatile RAM. By default, Carotene infers a Redis cache, perfect for rate-limiting, session management, or active WebSocket state.
backend {
store {
model User { id: UUID, email: String }
}
state {
model ActiveSession { userId: UUID, lastPing: DateTime }
}
}Execution: Functions vs. Loops ​
Computation in the backend happens in two ways: Event-Driven or Continuous.
1. function (Event-Driven) ​
A function is a discrete block of logic triggered by an event (a frontend request, an internal call, or a webhook). It spins up, executes its permitted side-effects, and returns a value or throws an error.
function ProcessPayment(amount: Float) -> Future<Boolean> {
requires Customer
updates store.Order
throws PaymentDeclinedError
}2. loop (Continuous) ​
A loop runs continuously at a configured frequency. This is used for background workers, polling queues, or high-tick-rate game servers.
loop MatchmakingWorker {
// Overrides defaults: Runs every 5 seconds
config polling { rate: "5s" }
reads state.PlayerQueue
updates store.Match
}The Gateway: External vs. Internal Traffic ​
Carotene enforces a strict boundary between your internal network and the public internet.
1. Internal Traffic (The Service Mesh): You do not need to define routes for your own frontends or microservices to talk to each other. When a frontend calls backend.ProcessPayment, Carotene auto-generates the secure internal RPC.
2. External Traffic (The Gateway): If you want to expose your backend to third-party developers, webhooks, or external IoT devices, you must explicitly expose the functions using a gateway block.
backend {
function StripeWebhook(payload: JSON) { ... }
// Exposing the webhook to the public internet securely
gateway {
// Explicit M2M security (bypasses human JWT auth)
config api_key { source: "header.Stripe-Signature" }
rest {
POST "/api/v1/webhooks/stripe" -> StripeWebhook()
}
}
}AI Implementation Boundaries ​
The backend is where business logic is most critical. When the AI agent implements a backend function, the Carotene sandbox wraps the generated code in a strict Dependency Injection container.
If the contract says a function only reads store.User, the database client injected into the AI's runtime environment physically will not contain .insert() or .update() methods. The AI is structurally prevented from dropping tables, bypassing auth middleware, or leaking data.