Browse the documentation

Preview documentation for AEL Beta 0.0.1 — launching soon

Hooks

A hook in AEL, the Agent Engineering Language, will be a typed function that runs before or after other code, for authorization, auditing or cleanup. Hooks will attach before or after any component, function or statement, run in ordered or parallel groups, and fail closed when authorization fails.

Status

Planned for AEL Beta 0.0.1. AEL is not available yet.

Package names on this page are preview naming and may change before launch.

Declaring a hook

A hook will live in <name>.hook.ael and declare exactly one hook <name>(…), with private helpers like any component.

Preview syntax — may change before launch
# src/auth.hook.ael
hook auth(context: EntryContext<Request>) -> HookDecision {
    return check_caller(context);
}

fn check_caller(context: EntryContext<Request>) -> HookDecision {
    # Your policy: allow this call, or deny it with a reason.
}

The hook will receive a typed context and return a typed decision. The names of the context and decision types will be confirmed when the syntax is final. The body of check_caller is a placeholder, as A first agent explains.

Attaching a hook

There will be three places to attach a hook:

Preview syntax — may change before launch
# Before a component or function: @ and the hook's name.
@auth
agent billing(request: Request) -> Receipt {
    # …
}
Preview syntax — may change before launch
# After a component or function: @ and the name, right after the closing brace.
agent billing(request: Request) -> Receipt {
    # …
}@audit
Preview syntax — may change before launch
# Before one statement.
fn charge(amount: u64, balance: u64) -> u64 {
    @limit_check
    let rest: u64 = balance - amount;
    return rest;
}
  • A hook before a statement will run each time that statement is reached, once, just before it. A statement in a branch that does not run will trigger no hook.
  • The values the statement uses will be evaluated once. The hook will see those values; it will never evaluate them a second time.
  • A package will be able to export hooks too. You will select one in the file header, like any other export, and attach it by name.

Ordered and parallel groups

Several hooks will be able to attach at one place. order will put them into groups:

Preview syntax — may change before launch
@auth(order = 0)
@rate_limit(order = 0)
@load_account(order = 1)
agent billing(request: Request) -> Receipt {
    # …
}@audit
  • A hook without order will be in group 0.
  • Hooks with the same order will run in parallel. The order in which you write them will never make them sequential.
  • Groups will run in ascending order: group 1 will start only after every hook of group 0 has succeeded. Gaps in the numbers will be allowed.
  • The protected code will start only after every group before it has succeeded.
  • When one hook fails, the remaining work of that attachment will be cancelled and cleaned up, and later groups will not run.

The check will refuse hooks of one group that could interfere with each other, such as two that change the same value.

Authorization will fail closed

If an authorization hook denies the call, fails, times out or is cancelled, the protected code will not run. The call will end as denied; it will never end as if the protected code had succeeded.

Hooks after the code

A hook after a component or function will run once the code has been admitted by its entry hooks, whatever the outcome:

  • It will run when the code returns normally, returns an error or is cancelled, and its context will say which of the three happened.
  • It will run before the code's result reaches the caller.
  • It will not run when an entry hook denied the call, because the code never started.
  • If the code failed, that failure will stay the result, and a failing exit hook will be reported alongside it. If the code succeeded and an exit hook fails, the call will end with that hook's failure.
  • An exit hook will not be able to undo something the code has already done outside the program, such as a sent message.
  • A panic, or a forced stop of the whole program, will end the program at once: exit hooks will not be guaranteed to run then, and their cleanup will never be reported as done.

What a hook sees

AttachedThe context holds
Before a component or functionWhich component and run it is, the parameters, the effective configuration, the authenticated caller, the deadline and the budget.
After a component or functionThe same, with the outcome: success with its result, an error, or cancellation.
Before a statementThe values the statement uses.
Before an HTTP route of the server/http packageThe method and route, typed path, query and body parameters, headers and cookies, and the authenticated caller.
  • The context will be read-only unless the attachment says otherwise, and any change will go through a typed, checked result.
  • A hook will not be able to change who the caller is, reach secrets it was not given, or keep request data after the request ends.
  • Credentials and cookies will not be logged by default.

Budgets and permissions

A hook will run inside the call it guards. It will draw on that call's budget and use that call's permissions: it will never receive a budget or a permission of its own.

A hook will run only when your program runs. No hook will run while you check or build your code, and no hook will be able to change what the compiler does. Hooks that call code carrying hooks of their own will be allowed only to a bounded depth.