You declare. The platform delivers.
A library of primitives for backend applications on AWS.
You describe each entity as configuration, compose entities into an application, and deploy it into an AWS account you own. The configuration assistant is a convenience — the DSL stands on its own.
Before and after.
What a primitive costs by hand. What it costs as config.
On the left: what a single primitive typically costs in a hand-rolled backend. On the right: the equivalent EntityConfig that produces the same surface — API, persistence, lifecycle, events — at runtime.
export async function createReservation(
req: CreateReservationRequest,
ctx: RequestContext,
): Promise<Reservation> {
const id = ulid();
const now = new Date().toISOString();
await assertResourceAvailable(req.resourceId, req.start, req.end);
const record = {
id, tenantId: ctx.tenantId, status: "PENDING",
resourceId: req.resourceId, start: req.start, end: req.end,
createdAt: now, updatedAt: now, version: 0,
};
await ddb.put({
TableName: RESERVATIONS_TABLE,
Item: record,
ConditionExpression: "attribute_not_exists(id)",
});
await publishEvent("reservation.created", record);
await scheduleExpiryTask(id, req.expiresAt);
return toReservationDto(record);
}val reservationConfig = EntityConfig(
name = "Reservation",
accessMode = EntityAccessMode.OWNER,
fragments = Seq("resourceId", "status"),
externalKeys = Seq("confirmationCode"),
sortKeys = Seq("start"),
).withLifecycle(
LifecycleConfig(
initial = "Pending",
transitions = Seq(
Transition("Confirm", from = "Pending", to = "Confirmed"),
Transition("Cancel", from = Set("Pending", "Confirmed"), to = "Cancelled"),
),
expiry = Some(ExpiryRule(after = 30.minutes, transitionTo = "Cancelled")),
),
)+ ~1,100 lines off-screen in service / repo / DTO / migration / Lambda wiring.
How it works.
Three stages, one runtime.
Each is a thin layer over AWS primitives — no hidden runtime that owns your data plane.
Declare
Each entity type is a single EntityConfig. Access mode, fragments, keys, lifecycle and validation are all data — no class hierarchy to subclass, no service to wire.
val supportTicket = EntityConfig( name = "SupportTicket", accessMode = EntityAccessMode.TENANT, fragments = Seq("priority", "status"), externalKeys = Seq("ticketNumber"), ).withLifecycle( LifecycleConfig( initial = "Open", transitions = Seq( Transition("Assign", from = "Open", to = "InProgress"), Transition("Resolve", from = "InProgress", to = "Resolved"), Transition("Reopen", from = "Resolved", to = "Open"), ), ), )Compose
Entities compose via relations, shared events, and shared index projections. An application is a collection of EntityConfigs plus the wiring between them.
object HelpDeskApp extends App( tenants = tenantConfig, entities = Seq(supportTicket, customer, agent), relations = Seq( Relation.oneToMany("SupportTicket", via = "customerId"), Relation.oneToMany("SupportTicket", via = "assignedAgentId"), ), notifications = Seq( OnTransition("SupportTicket", "Assign", template = "ticket.assigned"), OnTransition("SupportTicket", "Resolve", template = "ticket.resolved"), ), )Deploy
The CDK app provisions DynamoDB, the CDC pipelines, MSK, EventBridge, EKS services, OpenSearch — into an AWS account you own. No shared infrastructure.
# deploy to your own AWS account $ aws configure --profile acme-prod $ functory deploy --app HelpDeskApp --profile acme-prod ✓ provisioned DynamoDB (entities, commands, configs) ✓ provisioned OpenSearch domain ✓ provisioned MSK cluster ✓ wired CDC handlers ✓ rolled out EKS services deployed in 4m 12s
Gamification out of the box.
The scoring engine ships with the platform. Define score types with aggregation and decay, write event-driven rules that grant or deduct points, and issue achievement badges — all through configuration, without touching an event handler.
Scoring Engine
Badges
Achievement badges with triggers, categories, and rarity
| Badge | Category | Rarity | Points | Triggers | Status |
|---|---|---|---|---|---|
| 🏆First Purchase | MILESTONE | COMMON | 50 | 1 | ACTIVE |
| 🔥7-Day Streak | STREAK | UNCOMMON | 100 | 1 | ACTIVE |
| ⭐Top Reviewer | ACHIEVEMENT | RARE | 250 | 2 | ACTIVE |
| 💎Power User | ACHIEVEMENT | EPIC | 500 | 3 | ACTIVE |
| 👑Platform Legend | MILESTONE | LEGENDARY | 1000 | 4 | ACTIVE |
The console ships with it.
Every EntityConfig generates an API. The console gives your team the operational UI on top — auth configuration, entity management, lifecycle design, validation rules, commerce, engagement, and more. Nothing to build.
Console
System
Entity definitions, validation rules, and auth configuration.
Auth
Identity Providers
OIDC providers and OAuth social login integrations.
JWT & Token Settings
Token lifetimes, key rotation, and JWKS.
Rate Limiting
Rate limits for auth and API requests.
WebAuthn
Passkey settings, relying party, and allowed origins.
Entity Management
Entity Configs
Entity type configurations, access control, and versioning.
Entity Configurator
Visual editor for entity configurations with lifecycle support.
Lifecycle Designer
State machine configurations for entity lifecycles.
Validation & Rules
Validators
MVEL validation rules applied to entity operations.
Transformers
Data transformation rules applied to entity operations.
Expression Tester
Test MVEL, CEL, and GraalVM expressions against sample data.
Events & Compliance
Event Hooks
Webhook subscriptions triggered by entity lifecycle events.
GDPR / Privacy
Data subject access, erasure, and portability requests.
Configure without code.
The entity configurator walks you through four steps: name the kind and let the platform infer an archetype, set access control and index keys, define the lifecycle state machine, then review and save. The result is the same EntityConfig you would have written by hand — generated from a form.
Configurator
New Entity
Create Entity Config
Step 1 of 4 — Kind
Entity Kind Name
Enter the name for your entity kind. We will try to infer sensible defaults.
Kind
Tracks support requests with assignee, priority, and status. Typically tenant-scoped with a lifecycle.
Hints
- •Access mode: OWNER (submitter owns the ticket)
- •Fragments: status, priority, assignedAgentId
- •External keys: ticketNumber
- •Suggested lifecycle: OPEN → IN_PROGRESS → RESOLVED → CLOSED
Description (optional)
Coda
Three doors out.
Escape hatch
The mechanisms that guarantee you stay in control of your data and your code, including the day you decide to leave.
Read the contract →Compose
Interactive demo of the Configuration Assistant and App Config Tool. The LLM generates the same EntityConfig you would have typed by hand.
Try the playground →Showcase
See the kinds of applications that compose well from EntityConfigs — marketplaces, scheduling, subscriptions, learning platforms, and more.
Browse the showcase →