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
Discovery: resolve the name, then connect to the server
@alice's app → atDirectory: where is @bob? → host:port → connect to @bob's atServer
A client (or a peer server) resolves the target atSign in the directory to get its endpoint, then opens a TLS connection straight to that atServer. The directory is consulted for addresses only, never for data.

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.

Concernyour atServerthe 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.

Encrypted on the way in, stored encrypted, decrypted only at the far end
@alice encrypts for @bob → atServers store & route ciphertext → only @bob decrypts
The keys to read the value live on the endpoints, never on a server. A curious or compromised server operator still can't read the content: there's no plaintext there to read.

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.

@alice reads a record @bob shared with her
@alice's atServer acting for @alice @bob's atServer acting for @bob to: / from: / pol data: ciphertext
No shared database is consulted. @alice's server connects straight to @bob's server, proves who it is, and @bob's server decides whether to answer, returning ciphertext only @alice can decrypt.

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.

Public-key sign-in (PKAM), step by step
your app @alice's atServer from:@alice data:<challenge> one-time nonce sign with private key pkam:<signature> verify against the stored public key data:success authenticated as @alice
The private key never leaves your device; only a signature crosses the wire. The server checks it against the public key it stored when the atSign was set up.

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 passcode
the owner, or any enrolment holding the management grant
2
The agent requests enrolment
with that passcode, the namespaces it wants, and its public key. Recorded as pending, rate-limited.
3
You approve
granting only the namespaces you hold yourself
4
The agent signs in
proves 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 request
it calls a service, the enforcement endpoint, for something
2
The endpoint reads the intent
and asks a policy service: should I honour this?
3
The policy service decides
runs programmable logic, answers allow or deny with any detail the endpoint needs
4
The endpoint enforces it
and 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.

PropertyWhat 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

  • docs.atsign.com

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.