Skip to content

Identity & Auth ​

In traditional development, authentication and authorization are frequent vectors for critical security bugs. Developers—and AI agents—often forget to apply middleware to specific routes, leading to data leaks and privilege escalation.

Carotene solves this by treating human identity as a top-level architectural primitive. You define your roles and providers once in the auth block, and the compiler mathematically enforces them across your entire stack.

Defining the Auth Primitive ​

The auth block is defined at the root of your .carrot contract. It establishes the primary human identity system for your application and taxonomizes your users into a strict list of Roles.

dart
auth {
  provider: "jwt" 
  roles: [Admin, Staff, Customer]
}
  • provider: Tells the compiler what type of infrastructure to generate. jwt instructs Carotene to generate stateless token verification, while session would generate stateful, cookie-based session stores (often backed by Redis).
  • roles: A strict enumeration of the user types in your system. This list becomes the absolute source of truth for Role-Based Access Control (RBAC) across both the frontend and backend.

Enforcing Security: The requires Verb ​

Once your auth block is defined, you enforce access control using the requires verb inside your structural blocks (function, view, etc.).

If a function has a requires verb, the Carotene compiler generates strict middleware that validates the user's identity before the AI-generated business logic is ever executed.

1. Securing the Backend ​

dart
backend {
  function DeleteUser(userId: UUID) {
    // The compiler generates middleware to block anyone who isn't an Admin
    requires Admin 
    deletes store.User
  }

  function UpdateProfile() {
    // Multiple roles can be permitted
    requires Staff, Customer 
    updates store.User
  }
}

2. Securing the Frontend ​

The exact same verb is used to guard UI components. If a user does not possess the required role, the component is fundamentally stripped from the generated routing tree.

dart
frontend {
  view AdminDashboard() {
    requires Admin
    
    /// Render charts and metrics
    renders RevenueGraph
  }
}

The "Magic" of the Service Mesh ​

When your frontend calls a secured backend function, you do not need to write boilerplate to attach headers or manage tokens.

Because the Carotene compiler owns both the frontend and the backend AST (Abstract Syntax Tree), it seamlessly wires the auth context together. If a React component calls backend.DeleteUser, the generated frontend SDK automatically pulls the JWT from the local session and injects it into the Authorization: Bearer header.

The developer defines the rule. The compiler writes the boilerplate.


Human vs. Machine Identity ​

It is critical to distinguish between Human Users and Machine-to-Machine (M2M) traffic.

The top-level auth block is strictly for Human Identity (e.g., users logging in via a web portal). If you need to secure public API endpoints for external servers or webhooks, you use a typed config block inside the gateway.

dart
// For Humans
auth { provider: "jwt"; roles: [User] }

backend {
  // For external Machines (Webhooks, 3rd party devs)
  gateway {
    config api_key { source: "header.x-api-key" }
    
    rest {
      POST "/webhooks/stripe" -> ProcessPayment()
    }
  }
}

AI Sandboxing & Security ​

By extracting authentication into the Carotene contract, we completely remove the burden of security from the AI agent.

When the AI writes the implementation logic for DeleteUser, it doesn't need to check if the user is authorized. The Carotene execution sandbox guarantees that the logic will only be invoked if the ingress middleware has already cryptographically verified the user as an Admin.

The AI focuses entirely on the business rules, while the compiler guarantees the zero-trust perimeter.