Documentation

Guides and reference for OutLayer verifiable compute and agent custody.

Docs navigation

Tasks

An agent prepares; the owner reads and acts with a call of their own. A run of a project, admitted to an owner's secret row, leaves that owner a task through the outlayer:tasks host interface. The owner reads it in their inbox with no run, and acts on it by calling an operation of the same project from their own wallet. Any project can use it, connectors among them.

What a task is#

An action prepared and waiting for its owner. “Confirm this email”, “show me the bet before it is placed”, “give me your photo”. The agent's call leaves it; the owner's call carries it out; the agent learns the outcome the next time it asks.

  • Not a paused run. Nothing waits inside the enclave. A task is a record.
  • Not a second run on the agent's money. Each side pays for its own call.
  • Not a script the owner is made to run. A task names the operation that answers it and carries no arguments for it. The project's code decides what happens.
  • Not a replacement for a service's own confirmation. A bank's approval rules stay where they are.

Using it in a project#

Declare it in the manifest, and build on the SDK with the feature:

json
…
toml
…

Where the owner must be asked, open a task and answer the agent awaiting_owner:

rust
…
json
…

Write the operation the task names. It takes the owner's answer, acts, and reports:

rust
…

The rest is the same in every project, and the SDK answers it:

rust
…
OperationWhoAnswers
task_statuspreparerwhere one of its tasks stands, with the result or the reason
taskspreparerits tasks for this owner; {"tasks": []} when there are none
task_cancelpreparerwithdraws an open task
task_deletepreparerdeletes a task, in any state
tasks_unlockownerwrites the copies of the waiting tasks for the devices now signed in

Three rules a project keeps: check the request against the policy before the task is made; check it again, as of that moment, before acting; and show what will be done whole — what cannot be shown whole is refused, never shown in part.

A task is paid for when it is prepared. In a connector, the operation that opens a task keeps its price, and nothing comes back if the owner says no. The operation that answers a task, and the five above, are priced at zero: an owner pays nothing to say yes.

Whose task, and who may do what#

The owner of a task is the owner of the secret row the run named and the keystore opened. The preparer is the account that made the run. Both are the platform's facts: no function of the interface takes an account.

The run isIt may
the project's, made by an account the owner's row admits by nameopen a task for that owner; read, cancel and delete the tasks it made
the project's, made by the owneranswer the tasks of this project addressed to them; open them for a new device
admitted by a row open to everyone, to a pattern, or to holders of a token or a rolerun; open is refused not-granted-by-name
another agent of the same ownernothing of the first agent's tasks
another project'snothing of this project's tasks
one that names no row, or whose row did not opennothing: no-owner
one a contract relayed: the account that called OutLayer is not the account that signednothing: relayed

As with a bot in a messenger, nobody writes to a person who has not let them, and the owner silences whom they please: they mute an agent or a project in the inbox, and delete its waiting tasks at once. Who is muted, the devices signed in and the URL task events go to are on the inbox's settings screen, where a mute is lifted and a device withdrawn.

What the owner is shown#

A title of 80 characters and up to 12 fields. Each field is a label, one of six kinds, its values, and whose words they are: the project's or the agent's.

KindHoldsAt most
moneyan amount with its unit200 characters
accountan account at a service or on a chain200 characters
addressan address: of mail, of a wallet200 characters
textone line500 characters
long_texttext with line breaks50000 characters
list1 to 20 values200 characters each

Every value is drawn as plain text. No markup is interpreted, no link can be pressed, no image is loaded. Control characters, characters of zero width and characters that reorder text refuse the task. The host checks all of it, whatever the project was built with.

A task may carry files — an attachment of a message, a document — up to ten, 6 MiB together. The owner's page lists each by name, type and size and hands it over as a download; it holds the bytes it opened to the size and the hash the task names, and draws no file in itself. The operation that answers the task gets the files back.

States#

StateMeans
openwaits for the owner
answeringthe owner answered; the call named in run acts
donethat call ended well and the project reported
failedthat call ended any other way. Its status is the ordinary status of a call
rejectedthe owner said no, with a reason if they gave one
cancelledthe preparer withdrew it
expiredpast its life: 24 hours at most
voidthe policy changed since it was made

Each move is made once, and a task never returns to open: a call that failed may have acted in part. A task that leaves open loses what it showed at once; its outcome is kept 30 days.

LimitValue
open tasks addressed to one owner20
of them, from one preparer5
tasks one run opens5
what the waiting tasks of one owner hold together64 MiB
files of one task10, and 6 MiB together
the project's state256 KiB
the task, without its state and files256 KiB

What is sealed, and who reads it#

The platform stores a task and opens none of it. What a task shows is kept twice: sealed under a key that exists only in the enclave, and under a content key encrypted to each device the owner signed in. A device is a convenience, never the only copy.

text
…
Whoever holdsCanCannot
the platform's databasesee who was asked by whom, of what kind and when; delete a taskread what a task shows; add a device of their own; act on a task
a session's tokenlist tasks as ciphertext; reject and deleteread a task without the device's key; act on a task

The proof#

Before the inbox offers the button, it checks that the task was made by a published build of its project, and shows the run's attestation on request:

  • the run that made the task is attested, by an enclave whose measurements are approved on chain;
  • it was a run of the task's project, made by the account the task names;
  • the build that ran is a version of that project on the contract;
  • what the run answered hashes to the hash the enclave signed for;
  • that answer names the task with the hash of exactly what the page opened.

So a project's answer has to name the task it opened: tasks::awaiting_owner is that, put wherever the answer has room for it. A task written into the platform's database can be shown and cannot be acted on: the project acts on the task sealed in the enclave, and the owner's answer must name its hash.

The owner's session#

The owner signs one message with their wallet (NEP-413, recipient: the OutLayer contract). It moves nothing and approves nothing:

text
…
  • The device key is the public half of a key pair the browser made and keeps non-extractable: the page uses it and cannot read it.
  • The session lasts until the deadline in the sentence, 30 days at most, and is not extended.
  • One statement opens one session. An account has five devices in force at most, each with a session and a key of its own; one more retires the device signed in longest ago, which then says so. The owner sees their devices in the inbox's settings and withdraws any of them.
  • Before a task is encrypted to a device, the enclave checks the statement itself: the account, the deadline, the signature, and that the key that signed is a full-access key of the account on chain.
  • A session lasts while the key that signed it is a full-access key of the account: a key removed from the account ends the session it opened.
  • A device lost together with the wallet key that signed it in is cut off for certain by removing that key from the account.

Without a session nothing is told, not a count. A task made before a device signed in opens there after one call of the project's tasks_unlock by the owner.

Refusals#

A project answers a refusal as {"success": false, "error": "<code>: <sentence>"}. Nothing is never a refusal, and a refusal is never an empty list.

CodeWhenRetry
not_granted_by_namethe row admitted the run by a rule that does not list the callerno
no_ownerno row named, the row did not open, or no projectfix the call
relayeda contract made the run: the account that called OutLayer is not the account that signedcall OutLayer directly
mutedthe owner muted this agent or this projectno
inbox_fulla limit of open taskswhen the owner answers some
task_run_limitthis call opened or asked as much as one call mayin another call
display_invalidwhat is shown is outside the bounds; the sentence names whatfix the request
task_too_largethe state, the task or a result is over its boundmake it smaller
task_life_too_longa life asked beyond 24 hoursask for less
task_not_foundno such task of this project, owner and preparerno
not_the_owneran owner's operation in another account's callno
task_hash_mismatchthe hash named is not the task'sno
task_answer_invalidanother operation than the task names, or what was supplied is not what was askedno
task_closedanswered, rejected or cancelled alreadyno
task_expiredpast its lifeprepare it again
task_voidthe policy changed, or another build answeredprepare it again
task_unreadablea sealed copy that does not openno
task_store_unavailablethe store or the chain did not answeryes, later
task_internal_errora fault of the platform that a repeat does not mendno

Reference#