Atsign · platform introduction · for engineers & architects
An introduction to the
Atsign Platform
A platform where you own an identity — @alice — and a personal,
always-on server that holds your data. That data is encrypted end-to-end, and your server talks directly to
other people's servers, peer-to-peer. No central hub sees everyone's content.
🧭 How to read this deck
It's a technical introduction, built as a ramp: the architecture (identity, server,
directory, encryption), then the protocol that wires it together, then the programming model you
write against, and throughout, the use cases that motivate each choice. No prior Atsign knowledge is
assumed. Press → / Space to advance, ← to go back, click the dots up top to jump.
🔑 The one-line teaser
People build own-your-data apps, secure peer-to-peer sharing of contacts and files,
real-time messaging, IoT telemetry, and no-open-ports networking, all on the same 3 ideas: an owned identity,
a personal server, and end-to-end encryption between peers.
Why · own your data
Basics · atSign, atServer, atDirectory
Security · end-to-end encryption
Protocol · verbs over TLS, peer-to-peer
Model · local-first, collections, notifications
Uses · what people build
Documentation: docs.atsign.com. The programming
model runs on Dart (CLI/server) and Flutter (mobile/desktop/IoT).
Why · the premise
Today your data lives in silos you don't control
Every app you use keeps your data on its servers, under its rules. The Atsign model inverts that.
01
The silo model
Your contacts, messages, location, and files sit in a vendor's database. They set the
terms, they can read the content, and reaching a friend means both of you trusting the same company in the
middle.
02
The inversion
You own an identity — an atSign like @alice — and a
personal server that holds your data. It's yours the way a phone
number is yours: unique, and how others reach you.
03
Shared on your terms
Data is encrypted end-to-end and shared peer-to-peer with the specific people or
devices you choose. No third party in the middle holds the keys, so no third party can read it.
🔑 A concrete example
Share your live location with one friend. It's encrypted so only that friend can read it, it
moves directly between your server and theirs, and no location company holds a copy. Revoke it and it's gone.
That shape — one owner, specific recipients, end-to-end, no middleman — is the whole platform in miniature.
For the technical reader: this isn't a
policy promise you have to trust. It's an architectural property. The next slides show why the server can't
read your data even if it wanted to, and how peers find and authenticate each other.
Basics · identity & server
An atSign, and the atServer behind it
Two things pair up for every participant on the platform: a person, a device, or a service.
The atSign: an identity you own
A name like @alice, globally unique, that stands for you on the
network.
- Yours the way a phone number is yours: unique, and how others reach you
- Up to 55 characters; a person, a device, or a service can each have one
- Register once, then own it, free or paid, covered on the last slide
The atServer: your personal, always-on server
Holds one atSign's data and speaks the Atsign Protocol on its behalf, around the clock.
- Holds only @alice's data, not a shared cloud database owned by someone else
- Answers requests from @alice's own apps, and from other atSigns' servers
- Stores and moves ciphertext: it can't read the values it holds
🔑 The mental model
The platform is a network of personal servers, one per atSign. They talk to each other
directly: @alice's server asks @bob's server for something, and @bob's server decides whether to answer. There's
no central server that sees everyone's data.
Why the server can't read your data:
encryption happens between atSigns, before anything reaches a server. The atServer keeps the ciphertext current and
routes it, but the keys to decrypt live only on the endpoints. The next two slides make that precise.
Basics · roles & discovery
Three roles, and how peers find each other
The whole architecture is these 3 things plus one lookup step.
identity
atSign
- An owned, globally unique name: @alice
- Stands for a person, device, or service
- How everyone else addresses you
Owns: the identity and its master keys
personal server
atServer
- Holds exactly one atSign's data
- Speaks the Atsign Protocol on its behalf
- Stores and moves ciphertext it can't read
Owns: one atSign's encrypted records
discovery
atDirectory
- Maps an atSign → its atServer's endpoint (host:port)
- Read-mostly; how peers find each other
- Holds no user data, only the address book
Owns: atSign → endpoint mappings
Basics · responsibilities
Who does what: a clean split
Two jobs, two owners. Your data lives with your atServer; finding where an atSign lives is the atDirectory's job. Nothing else sits in the middle.
| Concern | your atServer | the atDirectory |
| Your data |
holds it, encrypted, and serves reads and writes |
never touches it |
| Where an atSign lives |
advertises its own endpoint |
owns the name→endpoint map; answers lookups |
| Authentication |
challenges and verifies (CRAM / PKAM / APKAM) |
none |
| Reaching another atSign |
dials the peer's atServer directly and delivers |
consulted to find the peer's endpoint |
| Your decryption keys |
never holds them; stores ciphertext only |
never sees your data at all |
🚧 Data and discovery, kept apart
The atDirectory is a phone book: it knows where an atSign lives, never what it holds.
Your atServer holds the data and needs no one's permission to serve it. No single service sees both where you are
and what you store.
💡 Why split them at all?
One owner per concern means one source of truth. Discovery has a single map, so peers always
resolve the same way; data has a single home, so there's no central store to breach. Self-hosting works precisely
because these two jobs are separable.
Security · end-to-end encryption
The server holds keys to nothing
Data is encrypted between atSigns. The atServer stores and moves ciphertext, and never holds a decryption key.
What the server can and can't see
- Can see the atKey, a record's public address
- Can filter and route by that key's structure (by regex)
- Can't see the value: it's ciphertext all the way through
- Can't filter, search, or sort by value content
This split — public address, private value — is what every later
design choice follows from.
🔑 The consequence: value-level filtering is always client-side
Because the server only ever sees ciphertext values, it can filter on the atKey structure
but never on the content. Any query that depends on a value — "todos where done is false", "messages
containing X" — must run on the client, after decryption. That's not a limitation to work around; it's the same
property that makes self-hosting and sharing safe.
Protocol · high level
The Atsign Protocol, at a high level
A small, line-oriented text protocol over TLS. One verb per line, one line back. Learn the shape once.
The wire is text over TLS
A client opens a TLS connection and the server greets with a bare
prompt. Each request is one line: a verb name, then :-separated parameters. The server replies
with one line. That's the whole framing.
@
// client sends one command line
lookup:email@bob
// server replies, then re-prints the prompt
data:<ciphertext for alice>
@alice@
Responses start with data: (success, payload follows),
error: (failure, with a code), or notification: (a streamed
event). Because it's plain text, you can drive a server by hand with openssl s_client and read every
byte.
The core verbs
from / toopen a session; who I am / who I want
cram / pkamauthenticate (secret / public-key)
enrollrequest / approve an app enrolment (APKAM)
update / deletewrite / remove a record
lookup / llookup / plookupread another's / local / public
scanlist keys matching a regex
notify / monitorsend / stream events
syncpull changes
pola peer proving its identity
The atKey: what a record's address looks like
Records are addressed by an atKey, whose structure is public even though the value
isn't: [visibility:]<name>.<namespace>@<owner>, e.g.
@bob:phone.wavi@alice, "alice's phone, shared with bob". The server filters on
this structure; it can never filter on the encrypted value. Keys cap at 255 characters, atSigns at 55.
💡 Why a plain-text, one-verb-at-a-time protocol?
It's trivial to implement in any language and trivial to debug: no binary framing, no code
generation, no SDK required to read the wire. A client library is a convenience, not a gatekeeper. And the strict
one-line-in, one-line-out shape keeps a connection's ordering easy to reason about.
Protocol · peer-to-peer
atSigns are peers: there's no central data hub
To read what @bob shared, @alice's atServer dials @bob's atServer and asks. Both sides speak the same protocol.
Authentication between peers
- from: / to: open the session: who I am, who I want
- pol is the peer proving its claimed atSign identity
- Only after that does the responder disclose what was shared with the caller
Versus the client-server silo
In the silo model, every client talks to one company's servers, which hold everyone's
data in one place. Here each atSign has its own server, they connect directly, and the only thing anyone else
stores about you is ciphertext they can't read.
🔁 The key idea
Cross-atSign work is the same protocol a client uses: dial the peer, identify, ask.
There's no privileged back channel and no central hub. That symmetry is what makes self-hosting a first-class
option: your server is an equal peer, wherever it runs.
Security · authentication
Proving who you are, without a password
Your atServer never holds a password it could leak. You prove your identity with a key, over a challenge/response the server verifies.
Two methods
- PKAM, public-key: you sign the challenge with a private key held on your device. The modern path.
- CRAM, the one-time bootstrap secret: used at onboarding to establish the atSign, then superseded by keys.
🔑 APKAM: scoped, revocable keys for apps and agents
Each app or AI agent gets its own key, not your master key. It's enrolled for only the namespaces
it needs (say {'todos': 'rw'}), you approve the request, and you can revoke it at
any time. A stolen or misbehaving key can touch only the data in its granted namespaces, never your whole
atSign. The next two slides go deeper.
💡 Why challenge/response, and why one-time?
There's no password on the server to steal, and the challenge is a one-time nonce, so a
captured signature can't be replayed. Any failed attempt returns the same "authentication failed", with no hint
about which step went wrong. Your master keys, generated once when you set up the atSign, are the root of
trust: back them up, because losing them loses the atSign.
Security · agent enrolment (APKAM)
APKAM: a scoped, revocable key for every AI agent
Onboarding makes one root keypair that can do anything. Handing that to an AI agent would be
all-or-nothing and irreversible. APKAM gives each agent its own key, only the namespaces it needs, and the ability to
pull the plug the moment it misbehaves.
An enrolment is one agent's grant
- Its own signing keypair. The private half never leaves the agent's host.
- A namespace grant: a map of namespace → r | rw.
- A lifecycle you approve, and can later revoke.
🔑 What that buys
Least privilege: a scheduling agent gets calendar:rw, never
the keys to everything. Revoke without re-keying: a misbehaving agent is turned off in place, and you and
every other agent keep working. Bounded delegation: a manager can grant only the namespaces it already
holds itself.
How an agent joins
1
You issue a one-time passcodethe owner, or any enrolment holding the management grant
2
The agent requests enrolmentwith that passcode, the namespaces it wants, and its public key. Recorded as pending, rate-limited.
3
You approvegranting only the namespaces you hold yourself
4
The agent signs inproves its key, and is now live with exactly its granted access
🔒 Encryption stays end-to-end
Granting an agent access never exposes your data to the server. It only ever handles
ciphertext, and the agent's private key never leaves it.
🛡️ The authorisation model
Two questions govern almost everything: do you hold the management grant (the right to
administer enrolments), and do you hold the namespace in question? Management is necessary but never
sufficient. You can only act on namespaces you already have, so a broad *:rw data grant is
not management authority.
🤖 Why this fits AI agents
An agent gets exactly the data it needs to do its job and nothing else, its access is a durable
record you can audit, and one revoke cuts it off cleanly: its connection drops and its private working data is moved
aside. An enrolment runs pending → approved → revoked → deleted, and no agent can read another's private
space.
Security · programmable permissions
APKAM + policy: a permissions mesh for a world of AI agents
APKAM says who an agent is and its baseline scope. Policy adds a live decision layer on top: an
endpoint asks a policy service, per request, whether to allow it. Both ride the Atsign Protocol, so the whole mesh is
atSigns talking to atSigns.
Two layers, cleanly separated
1 · The credential (APKAM)
Each agent carries its own scoped, revocable key. This is the coarse grant: which namespaces
an agent may touch at all, and the switch to cut it off.
2 · The decision (policy)
A separate policy service decides per request, on programmable logic (the request's
intent, context, relationships), and it can change centrally without redeploying a single endpoint.
Fine-grained, and dynamic.
Enforcement is decoupled from decision: the endpoint doesn't
hard-code the rules, it asks. The open-source at_policy package is the scaffolding
for exactly this.
A decision, per request
1
An agent makes a requestit calls a service, the enforcement endpoint, for something
2
The endpoint reads the intentand asks a policy service: should I honour this?
3
The policy service decidesruns programmable logic, answers allow or deny with any detail the endpoint needs
4
The endpoint enforces itand logs the event
Every hop is the Atsign Protocol:
outbound-only, end-to-end encrypted, addressed by atSign, not IP. No exposed ports, no
firewall holes.
🕸️ Why this shape scales to billions of agents
At that scale you can't pre-provision or hard-code every permission. You need two things at once:
a revocable credential per agent (APKAM), and a programmable decision layer you change centrally, not
endpoint by endpoint (policy). Change a rule once and every enforcement point reflects it; revoke an agent once
and it's cut off everywhere. Every actor, agent, service and policy service alike, is an atSign, so the result is a
mesh of atSigns exchanging encrypted decisions, not a central gateway to overload or breach.
Model · local-first
Local-first, kept current by real-time sync
The client keeps an on-device copy of your data. Reads and filters run locally; writes sync in the background.
01
On-device copy
The client holds a local copy of the atSign's records. Reads and value-level filters run
on-device by default, so they're instant and work offline.
02
Real-time sync
Changes flow between device and atServer in near real time (~50–200 ms). An
in-development "fsync" targets ~10–30 ms, excluding network time.
03
Offline-tolerant
Writes made offline queue locally and flush on reconnect. Large collections on-device are
fine: hundreds of thousands of records are feasible.
🔑 Why local-first is the default, not an option
Value-level queries have to be client-side: the server only sees ciphertext (slide
on encryption). Once the data is on-device to be queried, local-first also buys instant reads and offline
operation. The constraint and the benefit point the same way.
The escape hatch
Local-first is the default, not a wall. A client can route an individual fetch to the remote
atServer when it needs the authoritative copy directly, so "by default" is the accurate word, not
"always".
Model · the SDK
The programming model: the at_client SDK
You write against a client SDK; it talks to the atServer for you: key management, encryption, sync, and notifications.
What the SDK handles for you
- Key management, the right keys for the right atSigns
- End-to-end encryption and decryption at the edges
- Real-time sync of the on-device copy
- Sending and streaming notifications
Where it runs
Dart on the CLI and servers; Flutter on mobile, desktop, and IoT.
Flutter web is not a supported target: onboarding and key storage need platform plugins with no web
implementation. For Dart, cite AOT/obfuscated builds when you need minification.
The AtClient surface
High-level: most apps live here
collection<T>(…): typed, shareable records ·
notificationService: send / stream events ·
syncService: control sync.
Low-level: rarely needed
put / get /
delete(AtKey) operate on a single record and its metadata directly. Reach for these
only when a collection doesn't fit.
💡 Why collections over raw keys
Working with atKeys, metadata, and notification regexes by hand is easy to get wrong.
AtCollection puts a typed, verb-shaped API over all of it, so app
authors — human or AI — build with fewer footguns. The next slide is that API.
Model · collections
AtCollection<T>: typed, shareable records
CRUD on typed records that hides the atKey, metadata, and notification-regex ceremony behind a small set of verbs.
// open a typed collection (namespace, record TTL)
final todos = await atClient.collection<Todo>(
'todos.my_app', const Duration(days: 7),
fromJson: Todo.fromJson, typeTag: 'Todo');
// create a record, shared with @bob (encrypted for @bob)
final item = await todos.create(
obj: Todo('write readme'),
sharedWith: {'@bob'.toAtsign()});
// a composable, typed query — runs on-device
final open = await todos.query()
.where((t) => !t.obj.done)
.orderBy((t) => t.obj.due)
.limit(20)
.get();
App code creates records only via
collection.create<T>() / draft<T>(): there's no public
item factory. Then update, delete,
updateSharedWith.
What you get in the box
- Query builder: where · orderBy /
thenBy · limit · count ·
groupBy
- get() a one-shot List, or watch() a live Stream
- wherePath / PathField for index-pushdown
- Sub-collections scoped to a parent item, with opt-in cascade-delete
- availableAt (scheduled visibility) + expiresAt (TTL),
with timer events
- Built-in read receipts: markReadByMe / readBy /
a readReceipts stream
Immediate local events
CItemUpdated / CItemDeleted fire on the
local write immediately — no network wait — and re-fire on the round-trip. The UI updates at once and
stays correct.
💡 Design goal, and one identity rule
The point of AtCollection is to make it easy for
app authors — human or AI — to build with minimal footguns: typed signatures, explicit semantics,
verbs an LLM can use correctly. One rule to keep in mind: identity is (owner, id), never id alone; ids are
per-atSign-unique, not global.
Model · notifications
Notifications: fire-and-forget, and pub/sub
notificationService.send pushes an event to another atSign; monitor streams events as they arrive.
Store-as-record
The record itself is the durable state; the notification tells peers it changed. This is
the AtCollection shape: a shared todo updates, and everyone who
shares it sees the new value.
Good when the current value matters and you
want it queryable on-device.
Deliver-via-short-lived-notification
A high-frequency stream of observations, delivered as short-lived notifications and stored
wherever aggregation is cheap, often a relational database. This is the telemetry ("dockerstats") pattern: many
readings, where query and aggregation dominate.
The SDK supports this shape but doesn't
impose it. You choose per workload.
🔑 Two shapes, one mechanism
Both ride the same notification machinery; they differ in where the durable copy lives. Use
notifications for live events, messaging, and telemetry. The choice — record vs external store — is a workload
decision, made per stream, not a platform constraint.
Uses · what people build
What people build on the platform
Each use case leans on a specific platform property. The property is what makes the pattern work.
Own-your-data apps
Personal apps where the data lives in your atServer, not a vendor silo: notes, journals,
health records that stay yours.
local-first · your atServer
Secure peer-to-peer sharing
Contacts, credentials, location, and files shared with specific atSigns, encrypted so
only the recipient can read them.
E2E · peer-to-peer
Messaging & chat
Real-time messaging where content is encrypted end-to-end and no central service holds the
conversation.
notifications · E2E
IoT & telemetry
Devices publish readings as notifications; a collector stores and aggregates them, the
telemetry pattern for high-frequency streams.
notifications
No-open-ports networking
Reach a device or service without opening inbound firewall ports. The protocol brokers
the connection. The flagship use, at my.noports.com.
peer-to-peer · NoPorts
Collaborative live apps
Shared, live-updating todos and boards with built-in read receipts. Everyone sees changes
the moment they land.
local-first · notifications
🔑 The through-line
These aren't separate products stitched together. They're the same 3 properties recombined.
End-to-end encryption makes sharing safe, peer-to-peer removes the central hub, local-first gives instant and
offline reads, and notifications carry the live events. NoPorts is the clearest showcase: it reaches a device
behind a firewall with no inbound ports, using nothing but peer-to-peer protocol messages.
Platform · properties
Properties for the technical evaluator
Beyond the day-to-day API, these are the properties that decide whether the platform fits your constraints.
| Property | What it means for you |
| Self-hosting is first-class |
Run your own atServers, a split-horizon atDirectory, or a fully sealed ecosystem. Your server is an equal
peer, not a second-class client of someone else's cloud. |
| Multi-platform SDK |
Dart on CLI and servers; Flutter on mobile, desktop, and IoT. Flutter web is not a supported target:
onboarding and key storage need platform plugins with no web implementation. |
| Post-quantum crypto |
In active development. The current crypto stack isn't described as fixed: the platform is moving toward
post-quantum algorithms. |
| Pluggable crypto providers |
A default Atsign encryption provider, with a pluggable CryptoProvider
(via CryptoConfig) when you need custom encryption. |
| Human-and-AI-friendly model |
Typed signatures, explicit semantics, and verbs an LLM can use correctly, so app authors, human or AI,
build with fewer footguns. |
The evaluator's summary: data is
end-to-end encrypted and value-level filtering is client-side by design; identity and hosting are yours to run;
the crypto is pluggable and heading post-quantum; and the client SDK covers mobile, desktop, server, and IoT, not
web.
Platform · post-quantum
Making the encryption post-quantum-safe
A quantum computer that breaks today's public-key crypto doesn't exist yet. An adversary can record
encrypted traffic now and decrypt it once one does, so the platform's end-to-end encryption is moving to
post-quantum algorithms now.
⏳ Harvest now, decrypt later
Ciphertext captured today is a future liability: whoever stores it can read it the day a
quantum computer capable of breaking the classical keys arrives. Confidentiality is the urgent case, which is why
the work starts there. Signatures aren't harvestable the same way (a recorded signature is worthless once its key
retires), so authentication follows rather than leads.
First: post-quantum messaging (the urgent one)
Every message the SDK encrypts becomes quantum-safe.
- Your own data, data shared with other atSigns, and the keys handed to a new app: all sealed with a
hybrid scheme, X-Wing (ML-KEM-768 + X25519) for key transport and
AES-256-GCM for the data.
- Hybrid means classical and post-quantum together, so breaking one primitive isn't enough.
- Authentication signatures move to ML-DSA-65.
Then: post-quantum groups (pq-mls)
Forward-secure group encryption, built on a standard.
- The MLS protocol (RFC 9420) on post-quantum ciphersuites, behind a stable group interface.
- Adds per-message forward secrecy and the scale that makes large groups practical.
- An engine swap under the first deliverable, not a second migration. The messaging path already makes the
bytes quantum-safe.
🔀 Crypto-agility is what makes it safe
Every value carries the id of the scheme it was encrypted with, so a client keeps all older
providers and can read anything ever written. A new algorithm drops in as a provider, with no schema change
and no flag-day. The wire format never has to change when a primitive is replaced.
🙂 No new task, no new code
Each client upgrades on its own schedule. The rule is "post-quantum when it can, legacy when
it must", then, at the next major version, "post-quantum, refuse to write legacy". Reads stay universal, a value
is only ever written in a scheme every reader supports, and in the common case the user sees no new step and the
app author writes no new code.
Status: the pluggable-provider seam and the core
post-quantum primitives are landed; the messaging data path and its rollout are in progress.
Start · getting started
Getting started
Register an atSign, onboard it once, then authorise the apps that use it.
1
Register an atSign
Free at my.noports.com/no-ports-plans, or paid / custom at
my.atsign.com.
2
Onboard once
CRAM-authenticate, generate the master keypairs, and write them to a
.atKeys file (CLI) or the device keychain (Flutter). The master keys are the
root of trust. Back them up. Lose them and you lose the atSign.
3
Authorise apps via APKAM enrolment
An app requests only the namespaces it needs — e.g. {'todos': 'rw'} — the
master-keys holder approves, and the atServer issues a scoped, revocable key set. A compromised
scoped key can only touch its granted namespaces ("evil-app" protection).
The SDK
- at_client: Dart, CLI & server
- at_client_flutter: mobile / desktop / IoT
- at_onboarding_cli: onboard from the CLI
Canonical examples
- at_client/example/bin/collections_*.dart (CLI)
- at_client_flutter/examples/todos (Flutter)
Documentation
Start from a collections example, then wire in
notificationService for live events.
🔑 The shape to remember
An owned identity, a personal server, end-to-end encryption between peers, and a local-first
SDK that hides the ceremony. Register one atSign, open a collection, share a record with a friend. That's the
platform end to end.
Close · glossary
Glossary
The terms this deck used, in one place.
atSignan owned identity, e.g. @alice
atServerthe personal server that holds one atSign's data and speaks for it
atDirectorythe map from an atSign to where its atServer lives
atKey[vis:]name.namespace@owner, the public address of a record
namespacethe app-scoped part of an atKey, e.g. .wavi, .todos
registrarwhere you get an atSign (a free or a paid one)
Atsign Protocolthe small line-oriented text protocol every atServer speaks
E2E encryptiondata encrypted between atSigns; servers hold only ciphertext
local-firstreads run on-device, against a copy kept current by sync
CRAMthe one-time bootstrap secret, used at onboarding to set the atSign up
PKAMpublic-key sign-in: sign a challenge with your private key
APKAMa scoped, revocable app key, limited to certain namespaces
enrolmentan approved app key together with its namespace access
pol"proof of life", a peer atSign proving its identity
from / tothe first verbs of a session: who I am, who I want
notificationa real-time event sent to another atSign; monitor streams them
syncpulling the change feed to keep a local copy current
AtCollectionthe SDK's typed, shareable-records API over atKeys
📎 Where to go next
Get an atSign, install the SDK, and read the worked examples: full guides live at
docs.atsign.com. From there the shared-todos and telemetry examples show the programming
model end to end.