Skip to content

Divergences from Akka

ankka reimplements Akka's component model — entities, views, consumers, workflows, timers, agents and HTTP endpoints — in Scala 3 on Apache Pekko, the Apache-licensed fork of Akka 2.6. Someone who knows Akka's SDK will recognise every component. The differences below are deliberate, and each has a reason.

Summary

Akka ankka Why
@Component and classpath scanning explicit register(...) A missing component fails at startup, not at its first request.
Entity::method lambda inspection typed handles declared on a companion No reflection; the compiler checks every call site.
@FunctionTool and reflection the FunctionTool builder A tool's schema and its argument decoder come from one instance and cannot disagree.
A bespoke SQL-like view query language real SQL over a JSON row column Nothing to learn or parse, strictly more expressive, and indexes are explicit.
Route order decides dispatch literal segments outrank parameters /users/me works whether it is declared before or after /users/{id}.
An ACL by absent annotation an abstract acl every endpoint must define An unstated ACL is a decision nobody made.
Principals naming the caller — the internet, a named service, self a predicate over the request, or a credential the service verifies Without mutual TLS there is nothing to name a caller with, and a header the caller sets is not evidence.
budget_tokens and temperature effort and adaptive thinking Current Claude models reject both.
apply -f service.yaml apply -f service.json The descriptor has the same shape; JSON avoids a YAML parser in the CLI.
minInstances defaults to 3 defaults to 1 One is what you want while trying the platform out. Set three for production.
Four service lifecycle states eight NotDeployed, Paused, Failed and Suspended are distinctions four states cannot express.

Registration is explicit

Akka finds components by scanning the classpath for annotations. In ankka a service is the list of components handed to its builder:

Ankka.service
  .register(ShoppingCartEntity.descriptor)
  .register(CartRows.descriptor)
  .withExtension(HttpServer.of(clients => ShoppingCartEndpoint(clients.componentClient)))
  .start()

The service's contents are a value that can be read, diffed and tested, and a component that was never registered does not exist, rather than failing on the first request that reaches for it.

Handlers are declared with a wire name

Akka identifies a handler by inspecting the bytecode of a method reference. ankka declares each handler on the component's companion, with its wire name:

val addItem = command("add-item")(_.addItem)
val getCart = query("get-cart")(_.getCart)

The wire name is what callers, persisted timers and in-flight requests address, so renaming the Scala method changes nothing on the wire. query accepts only a read-only effect, which makes "this handler cannot persist" a compiler guarantee. See Handlers and wire names.

Tools are built, not annotated

A FunctionTool is declared with a builder whose parameter types produce both the JSON Schema the model sees and the decoder that reads the model's arguments. A parameter cannot be described as an integer and read as a string, and a mismatch between the parameters and the handler is a compile error.

Views are queried with SQL

Akka views have their own query language. An ankka view stores each row as JSON in a Postgres table and is queried with SQL fragments over that JSON, through the view client. There is no parser to learn, any condition Postgres can express is available, and an index is something you create rather than something the platform infers.

Literal path segments win

In Akka's HTTP endpoints, which of two matching routes handles a request can depend on declaration order. In ankka a literal segment always outranks a parameter, so /carts/summary reaches its own route even when /carts/{cartId} was declared first.

Every endpoint states its ACL

Akka denies access when an endpoint has no ACL annotation, which is safe but silent. An ankka endpoint must define acl; Acl.DenyAll, Acl.AllowAll, a predicate or an authenticator. Nobody ships an endpoint without having decided who can reach it. The Python SDK requires the same attribute, and an endpoint that omits it fails when its class is defined.

A route can state an ACL of its own — withAcl in Scala, an acl argument to the route decorator in Python — which replaces the endpoint's for that route exactly as Akka's method-level annotation replaces its class's.

ACLs name what the request carries, not who is calling

Most of Akka's ACL vocabulary names the caller: the internet, a specific deployed service, any service, the service itself, a backoffice proxy. Those principals are trustworthy on Akka because the platform terminates mutual TLS and guarantees the identity cannot be forged. ankka has none of them, because it has none of that machinery: there is no mesh, no workload identity and no service-to-service invocation, and a service's in-cluster address is reachable from every namespace. Inventing the vocabulary anyway would mean deciding who a caller is from a header the caller sets, which is not a security control.

So ankka's ACLs are the two honest kinds. Acl.AllowIf is a predicate over the request as it arrived, and Acl.Authenticate verifies a credential — a signed token, a client certificate — that a service can check for itself. What Akka expresses as @Acl(allow = @Acl.Matcher(service = "shopping-cart")) has no ankka spelling, and will not until the platform can establish identity; it is recorded in Limitations. In one respect ankka's is the richer model: AuthDecision distinguishes "log in" from "you may not" from "the check could not be made", where Akka's ACL has a single refusal.

Model settings follow current models

Akka's agent configuration exposes a thinking token budget and a temperature. Current Claude models refuse both, so ankka's Anthropic provider uses effort and adaptive thinking instead.

Descriptors are JSON

The service descriptor has the same shape as Akka's, and is JSON rather than YAML. See Service descriptor.

One instance by default

Akka's platform defaults a service to three instances, which suits a managed production cluster. ankka defaults to one, because the platform is as often a laptop or a development cluster. Three is still the right number for production: the instances form one cluster, and an odd count is what lets a majority survive a network partition.

More lifecycle states

Akka reports Ready, UpdateInProgress, PartiallyReady and Unavailable. ankka adds NotDeployed, Paused for a service its members stopped, Failed for a rollout or provisioning that gave up, and Suspended for a service stopped because its organization was disabled. See Service lifecycle states.

What Akka has that ankka does not

Multi-region replication, multi-table views, view rebuild on deploy, and autoscaling are among the capabilities ankka does not have. See Limitations.