# Welcome

### Atsign Documentation

Welcome to the Atsign documentation. Here you’ll find everything you need to understand the Atsign Platform, explore its core technologies, and start building secure, private applications and workflows. Choose a topic below to begin.

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>AI Architect</strong><sup><strong>TM</strong></sup></td><td><sup>Explore how to visually map out secure apps and workflows and export LLM prompts that generate apps on Atsign Platform<strong>.</strong></sup></td><td><a href="/files/7mGCYytcnPk8qVp32Tj7">/files/7mGCYytcnPk8qVp32Tj7</a></td><td><a href="/files/n6C7UsAlfj0Zib4KbIcw">/files/n6C7UsAlfj0Zib4KbIcw</a></td><td><a href="/pages/D8utXwfCiJwHAlMmcncV">/pages/D8utXwfCiJwHAlMmcncV</a></td></tr><tr><td><strong>Core Technology</strong></td><td><sub>High-level tour of Atsign Platform: how Atsigns, the Atsign Protocol, and atServers work together.</sub></td><td><a href="/files/26EiqL4sDLXdgosogJJB">/files/26EiqL4sDLXdgosogJJB</a></td><td><a href="/files/vxVqspaCWeS71hqHrZQm">/files/vxVqspaCWeS71hqHrZQm</a></td><td><a href="/pages/JMLKqIBOIrnYVGmvusTe">/pages/JMLKqIBOIrnYVGmvusTe</a></td></tr><tr><td><strong>atSDK</strong></td><td><sub>A practical introduction to the atSDK. Everything you need to build, authenticate, and exchange data across apps, devices, and environments.</sub></td><td><a href="/files/h8mWG3I3QzHzTmz31dqe">/files/h8mWG3I3QzHzTmz31dqe</a></td><td><a href="/files/f6v6bR2weiN9h1qCnUck">/files/f6v6bR2weiN9h1qCnUck</a></td><td><a href="/pages/TAzMpZlJy9a4Zke848QX">/pages/TAzMpZlJy9a4Zke848QX</a></td></tr><tr><td><strong>NoPorts </strong><sup><strong>TM</strong></sup><strong> ↗</strong></td><td><sub>See how NoPorts applies Atsign Platform to deliver secure, zero-trust connectivity without open ports or VPNs.</sub></td><td><a href="/files/F1RvagxcaJOtzHrkrM4S">/files/F1RvagxcaJOtzHrkrM4S</a></td><td><a href="/files/XixTooXR6fBW57zIHY3X">/files/XixTooXR6fBW57zIHY3X</a></td><td><a href="https://docs.noports.com/">https://docs.noports.com/</a></td></tr><tr><td><strong>GitHub ↗</strong></td><td><sup>See how Atsign Platform is built and join the development on our GitHub.</sup></td><td><a href="/files/Y4J1j9EEyyaFs3TBCzti">/files/Y4J1j9EEyyaFs3TBCzti</a></td><td><a href="/files/Bs5qZODR9j6gQUeIXVNx">/files/Bs5qZODR9j6gQUeIXVNx</a></td><td><a href="https://github.com/atsign-foundation">https://github.com/atsign-foundation</a></td></tr><tr><td><strong>atsign.com ↗</strong></td><td><sup>Learn more about the technology behind secure, private communications between people, entity, and things.</sup></td><td><a href="/files/pQ2DO7jVc4a8GQHTMi3f">/files/pQ2DO7jVc4a8GQHTMi3f</a></td><td><a href="/files/3Zj2LOVWumrTRfwZkXOc">/files/3Zj2LOVWumrTRfwZkXOc</a></td><td><a href="https://atsign.com/">https://atsign.com/</a></td></tr></tbody></table>

### Need Help?

<details>

<summary>via Email</summary>

Send us an email: <support.team@atsign.com>

Monday - Friday, 6am - 6pm (PT)

</details>

<details>

<summary>via Discord</summary>

[Join our Discord](https://discord.gg/atsign-778383211214536722) for technical support. Our team and community are here to help!

</details>


# Atsign Platform Overview

An overview of Atsign's core pillars of technology

## TL;DR

Atsign Platform allows people, entities and things to communicate privately and securely without having to know about the intricacies of the underlying IP network. The Atsign Protocol is the application protocol used to communicate, and Atsigns are the addresses on the protocol. All cryptographic keys are cut at the edge by the Atsign owner, meaning only the receiving and sending Atsigns see data in the clear.

Atsign Platform can be used to send data synchronously or asynchronously, and can be used as a data plane, or a control plane, or both, simultaneously at Internet scale.

<figure><picture><source srcset="/files/VLU9L4xyqpurhdVCmvER" media="(prefers-color-scheme: dark)"><img src="/files/EmLLyAm6xt2QwR570d52" alt="Diagram of Atsign&#x27;s Core Technology"></picture><figcaption></figcaption></figure>

<details>

<summary>Relationships</summary>

Every **atServer** is associated with *one* **Atsign**, and each atServer stores *many* **atRecords.**

When provided an **Atsign**, the **atDirectory** returns a *DNS address* and *port number* for its **atServer.**

The **Atsign Protocol** is the *application layer protocol* used to communicate with an **atServer.**

</details>

## atServer

An atServer is both a personal data service for storing encrypted data owned by an Atsign, and a rendezvous point for information exchange. An atServer is responsible for the delivery of encrypted information to other atServers, from which the owners of those Atsigns can then retrieve the data.

{% hint style="info" %}
Unless explicitly made public, atServers only store encrypted data and do not have access to the cryptographic keys, nor the ability to decrypt the stored information.
{% endhint %}

<details>

<summary>atServer Functionality</summary>

* Cryptographic authentication of client devices.
* Cryptographic authentication of other atServers.
* Persistence of encrypted data on behalf of the controlling Atsign.
* Caching of data shared by others with the controlling Atsign.
* Notification of data change events to clients (edge devices) and other atServers to facilitate delivery of information shared with them.
* Synchronization of data with multiple clients (edge devices).
* TLS wire encryption from clients to atServers using SSL certificates.
* Mutually authenticated TLS 1.2/1.3 wire encryption between atServers using SSL certificates.

</details>

## atDirectory

In order for an Atsign to communicate with another one on the internet, we need to locate the atServer that can send and receive information securely on its behalf.

The location of an atServer is found using the atDirectory service (`root.atsign.org:64`). This directory returns the DNS address and port number of the atServer for any Atsign that it has a record for. The atDirectory service contains no information about the owner of the Atsign.

## Atsign Protocol

{% hint style="info" %}
The Atsign Protocol communicates via layer 7, the application layer of the OSI model, over TCP/IP.
{% endhint %}

The Atsign Protocol is an application protocol that enables data sharing between Atsigns. You can learn more about the Atsign Protocol by reading the [specification](https://github.com/atsign-foundation/at_protocol/blob/trunk/specification/at_protocol_specification.md). The Atsign Protocol uses TCP/IP and TLS but does not specify how data itself is encrypted, that is the job of the atSDK and atClient libraries.

## atSDK

atSDKs provide developers with Atsign Platform specific building tools in a number of languages and for a number of operating systems and hardware. The atSDK allows developers to rapidly develop applications that use Atsign Platform.


# Frequently Asked Questions

## Frequently Asked Questions

This page answers common questions about Atsign Platform: how Atsigns, atServers, and the atDirectory work together to provide end-to-end encrypted, identity-first communication.

### Atsign

#### What is an Atsign?

An Atsign (e.g. `@alice`) is a resolvable address, similar in concept to an email address. Anyone or anything can have an Atsign — people, organizations, AI agents, and devices (IoT sensors, gateways, routers, etc.).

#### What is an Atsign used for?

An Atsign is used to securely exchange information with other Atsigns without risk of surveillance, impersonation, or data theft. The exchange can be a pure data exchange, or it can trigger an action on the receiving system.

#### What characters can be used to form an Atsign?

Atsigns can be made of any combination of UTF-7 characters up to 55 characters in length, provided they are not already registered. This provides a name space of roughly 10^224 Atsigns. Some combinations may be unavailable or restricted (e.g. trademarked names, already registered Atsigns).

### atServers

#### What is an atServer?

An atServer is a private datastore for encrypted data owned by a single Atsign, and a rendezvous point for information exchange. It is responsible for delivering encrypted information to other atServers, where the recipient can then retrieve it. **An atServer only stores encrypted information and never has access to the cryptographic keys.**

#### What functions does an atServer perform?

An atServer provides:

* Cryptographic authentication of client devices
* Cryptographic authentication of other atServers
* Persistence of encrypted data on behalf of the controlling Atsign
* Caching of data shared by others with the controlling Atsign
* Notification of data change events to clients and other atServers
* Monitoring of notifications from other atServers
* Synchronization of data across multiple clients (edge devices)
* TLS wire encryption from clients to atServers
* Mutually authenticated TLS 1.2/1.3 wire encryption between atServers

#### What does an atServer consist of?

An atServer is a small dockerized service that can be deployed almost anywhere. It requires persistent storage and publicly addressable network connectivity. Each Atsign gets its own isolated atServer instance.

This distributed, single-tenant architecture means there is no centralized service mixing data from many Atsigns together — unlike traditional multi-tenant web services that present an attractive target for hackers.

#### Where are atServers hosted?

There are two options:

* **Atsign-hosted atServers** — Run by Atsign in the cloud with automated deployment and maintenance. Atsign cannot see private data because it is encrypted with keys Atsign never has access to.
* **Self-hosted atServers** — Run by the Atsign owner on platforms ranging from a Raspberry Pi to GCP, AWS, or on-premises infrastructure.

#### How does authentication work with an atServer?

The very first authentication uses a shared secret (a "cram key") (CRAM stands for Challenge Response Authentication Mechanism). After that, an RSA-2048 or ECC key pair is generated and used for all subsequent authentication via PKAM (Public Key Authentication Mechanism).

To create additional keys that grant access to the same atServer with namespace restrictions, you can use APKAM (Application Public Key Authentication Mechanism). APKAM allows individual applications to authenticate with their own keypair, scoped to only the namespaces they need.

#### How do atServers protect the data they hold?

Each atServer holds data for a single owner, which limits the appeal of an attack. If an attacker did gain access to an atServer's datastore, all private data is encrypted with cryptographic keys that the atServer does not hold. It is therefore *provably true* that the data cannot be decrypted from the atServer alone — unlike systems that rely on policy and procedure to safeguard centralized data.

#### What is the size of an atServer? How much can it store?

An atServer can store as much data as its underlying storage system allows. For Atsign-hosted atServers, the default configuration allocates 50MB of memory to the docker container, which is sufficient for most Atsigns representing people or things. Busier Atsigns (e.g. for companies) can be allocated more CPU and RAM. Contact us at <support@atsign.com>

### atDirectory

#### What does "resolvable address" mean for an Atsign?

For an Atsign to communicate with another, it needs to locate the atServer that handles its traffic. This lookup is done through the atDirectory service (`root.atsign.org`), which returns the DNS address and port number of the atServer for any Atsign it has a record for. The atDirectory contains no information about the owner of the Atsign.

### Edge Devices

#### What is an edge device and what is edge-to-edge encryption?

An edge device is any device located at the edge of a network that produces or consumes data — smartphones, personal computers, servers, IoT devices, gateways, etc.

Edge-to-edge encryption is an extension of end-to-end encryption: data is encrypted at all times during transmission and is only decrypted on edge devices. Atsigns store their cryptographic keys exclusively on the edge device. The atDirectory is never used to route personal data — it is only used for DNS lookups. atServers transmit, receive, and store encrypted data without any access to the keys needed to decrypt it. Only data that has been intentionally made public is stored in the clear.

#### How does Atsign Platform eliminate network attack surfaces from devices?

All connections from an Atsign Platform-enabled device are *outbound* to its atServer. Inbound communications must be encrypted and sent to the device's atServer; the device picks them up the next time it connects (or immediately, if already connected).

This means edge devices have no need for open listening ports, no need for a known IP address, and no need for direct network reachability. The result: no network attack surface.

#### Does Atsign Platform require static IPs?

No. Because all connections are outbound from the edge device to its atServer, device IP addresses can change freely. atServers themselves do have static DNS addresses.

#### How are devices addressable when no ports are open?

Atsign Platform provides bidirectional communication originating outbound from the device. The device is reachable as long as it has internet access — no port forwarding required, even behind firewalls or NAT.

To send a message to a device, you send it to that device's atServer. The atServer authenticates you as a permitted sender and notifies the device. If the device is connected, it receives the message immediately; if not, it receives it as soon as it next connects.

#### Does Atsign Platform require passwords?

No. Authentication is mutual, cryptographic, and does not depend on shared secrets like passwords between endpoints. This eliminates the entire class of credential-compromise attacks against centralized credential stores.

#### What is the latency?

In a typical case where `@atsign_1` sends an end-to-end-encrypted message to `@atsign_2`, once the socket connections are established, end-to-end latency excluding speed-of-light is typically between 4 and 15 milliseconds — including encryption, decryption, and the atServer's store-and-forward work.

#### What hardware and operating systems are supported?

**Processors**

* x64
* ARM (ARMv7, ARMv8, M1/M2 Apple Silicon)
* RISC-V (Beta)
* Older or more exotic CPUs are supported via the C atSDK

**Operating Systems**

* Linux
* macOS
* Windows
* iOS
* Android

**Other**

* Microcontrollers via the C atSDK

#### Will this work on very low-power hardware?

Yes, provided there is a microcontroller with enough RAM to handle crypto operations. Extensive testing has been done on Raspberry Pi Pico (192kB RAM) and various ESP32-based platforms. 128kB MCUs are likely workable; below that, an additional processor is generally needed.

### Encryption

#### Why does Atsign Platform use encryption?

To make it *provably true* that data is accessible only to the intended parties. Two properties make this work:

1. Asymmetric cryptographic keys are generated and kept on the edge device where the data is created.
2. Symmetric encryption keys used to share data are themselves encrypted with those asymmetric keys, then exchanged via the Atsign Protocol.

This means only the creator and the intended recipient ever have the keys to decrypt the data. No infrastructure operator — including Atsign — can access private data.

#### What encryption algorithms are used?

Atsign Platform uses both symmetric and asymmetric encryption. Currently:

* **Symmetric**: AES-256 (used to encrypt the actual data, including data streams)
* **Asymmetric**: RSA-2048 and ECC (used for authentication, data signing, and exchanging symmetric keys)

The protocol is designed to be extensible, so additional algorithms (e.g. post-quantum) can be added as needed.

#### How is data transmitted between two Atsigns?

Suppose Alice (`@alice`) wants to share data with Bob (`@bob`):

1. Alice's device generates a new AES key and encrypts the data with it.
2. Alice's device stores the encrypted data on her atServer.
3. Alice's device encrypts the AES key using Bob's RSA public key, and stores that encrypted key on her atServer too.
4. Alice's AtServer notifies Bob's atServer that data is available.
5. Bob's device retrieves the encrypted AES key, decrypts it with his RSA private key, then uses the AES key to decrypt the data.

#### What types of cryptographic keys does Atsign Platform manage?

**Authentication**

* `pkamPublicKey` / `pkamPrivateKey` (asymmetric) — public key held on the atServer, private key held only on the edge. The technology supports holding the private key in a secure element and delegating sign/encrypt/decrypt operations to it.

**Asymmetric encryption**

* `encryptionPublicKey` / `encryptionPrivateKey` — public key held on the atServer. When one Atsign needs to share a symmetric key with another, it retrieves the recipient's encryption public key, encrypts the symmetric key with it, and sends it. This keypair is also used for data signing and signature verification.

**Symmetric encryption**

* Symmetric keys are used to encrypt the actual data being exchanged. They are securely exchanged using the asymmetric encryption keypairs above.
* Atsigns also generate symmetric keys to encrypt data they want to store privately for themselves.

#### How are these keys protected?

Asymmetric keypairs are generated on the edge device, and private keys never leave it. There is no centralized key management. Ideally, keys are generated and held in a secure enclave (TPM, SIM, etc.), with private keys isolated from other forms of access.

#### What happens if I lose my keys?

Atsign cannot retrieve lost keys — they were generated on your device and Atsign never had access to them. However, you still own your Atsign and can reset it by contacting `support@atsign.com`. You can then generate a new keyset and continue using the same Atsign.

#### How are stolen or compromised devices handled?

There are several options depending on the use case:

1. The Atsign can be reset on the `my.atsign.com` portal, invalidating any compromised keys.
2. Keys can be cut in hardware (SIM or TPM) so the private key is never exposed directly.
3. Keys can be exported and imported across devices. (This is being phased out in favor of a model where each Atsign supports multiple keys across multiple devices, with a master key that can revoke access for any individual device — protected by a second factor such as biometrics.)

#### Are there any other security features?

In addition to end-to-end encryption of private data, TLS validates the authenticity of atServers and encrypts wire traffic between clients and their atServer. Communication between atServers is mutually authenticated and TLS-encrypted.

### Data Model

#### What is the data exchange and persistence model?

The Atsign Protocol's default persistence model is a simple key-value store. Record IDs have a syntactical structure that is also used in the protocol exchanges between clients and atServers.

The record ID structure is:

```
[cached:]<visibility scope>:<entity ID>.<namespace>@<owner Atsign>
```

Record IDs are always stored in lowercase.

**Visibility scope** — defines who can see and access the data:

* **Public** (e.g. `public:location.some_app@alice`) — available to anyone, surfaces in unauthenticated atServer scans, and is not encrypted.
* **Private** (e.g. `@bob:phone.some_app@alice`) — shared privately with a specific Atsign. Only shows up in scans for the owner and recipient after authentication, and can only be decrypted by the owner and recipient.

**Entity ID and namespace** (e.g. `@bob:work.email.an_app@alice`):

* Entity IDs can be any alphanumeric string. Because record IDs are stored in lowercase, snake\_case is recommended (`home_phone_number`, not `homePhoneNumber`).
* The namespace (`an_app` above) controls which applications can read, write, and synchronize a given record.

**Owner Atsign** — indicates which Atsign created the record.

**Cached prefix** (e.g. `cached:@bob:phone.buzz@alice`) — indicates the record was created by another Atsign, encrypted and shared with you, with metadata permitting it to be cached on your datastore. This enables offline support and improves performance.

#### Can I share different subsets of a device's data with different parties?

Yes. You can configure a device so that different datasets are sent to different recipients, each uniquely encrypted for the intended recipient.

### Integration

#### Can I integrate Atsign Platform with existing cloud and IoT platforms?

Yes. Atsign Platform is powered by the open Atsign Protocol and provides SDKs for integration with most stacks. There are open-source libraries, widgets, and connectors available on [Atsign's GitHub organization](https://github.com/atsign-foundation). The default cloud is GCP, but the platform is compatible with any cloud or on-premises infrastructure.

#### How do I assign an Atsign to a device?

There are several options:

* Assigned manually via a physical connection or OTA update.
* Pre-assigned by the manufacturer of the device or SIM.

For IoT devices, a best practice is to define separate Atsigns for administration and data ownership. This separation makes it easy to keep device administration distinct from data ownership and privacy.

#### Can I change the Atsign on a device?

It depends. If the Atsign is embedded in an eSIM/iSIM, it generally cannot be changed. Otherwise it is possible — but in practice, it is more common to change the *designated owner* of a device (e.g. when the device is sold) rather than the Atsign itself.


# Atsign

A unique identifier which serves as the address of the atServer

## What is an Atsign?

An Atsign (e.g. @alice) is a resolvable address for an [atServer](/atsign-platform/infrastructure). Similar to how a handle like @alice or @bob makes people addressable on social applications, our goal for an Atsign is to give addressability to everything on the Internet (people, entities, and things).

## What is an Atsign used for?

An Atsign is used to protect and securely exchange information with other Atsigns without any chance of surveillance, impersonation, or theft of the information by anyone. Since cryptographic keys live only on your edge devices, that means no middle actors (not even Atsign) can decrypt your data.

## How do I get an Atsign?

You can purchase and manage your Atsigns at the [Atsign Registrar](https://my.atsign.com/go). The price of your Atsign will vary given the amount of special characters in the Atsign (emojis, numbers, etc,.).

### How do I get a Free Atsign?

We no longer offer free Atsigns. Existing free Atsigns remain active under our current terms.&#x20;

### What characters can be used in an Atsign?

An Atsign supports any combination of Unicode UTF-8 characters that are translated to UTF-7 and must have less than characters 55 characters in length. This provides an enormous name space of 10^224 atSigns.

## How do I activate my Atsign?

Check out [Activating my Atsign](/atsign-platform/atkeys) in our atKeys reference page.


# atKeys

The file that contains the set of cryptographic keys belonging to an Atsign.

## What is the atKeys file?

The atKeys file is a set of cryptographic keys that provides devices the ability to authenticate into their AtServer and encrypt data for other Atsigns.

This is a file. Not to be confused with "AtKeys" which are the identifiers used in [atRecords](/atsign-platform/atrecord).

### File Conventions

The name of the file will always follow this format:

```
<@atsign>_key.atKeys
```

Example: `@alice_key.atKeys`

The location of your atKeys files will almost always be automatically generated to this directory on your computer (MacOS, Linux, Windows).

```
$HOME/.atsign/keys/
```

If on a mobile device, you will be manually saving it to a safe location via your mobile device's file system. You will need to manually keep track of this key's location, so be sure to keep it somewhere safe!

## What is activation?

**Activating** is the one-time, irreversable bootstrapping of a freshly provisioned Atsign. During activation, the client generates a cryptographic identity (a keypair) and registers it with the server as the proof of ownership.&#x20;

The end result of activation is the **.atKeys** file; a portable credential file that represents your ownership of that Atsign. Ensure that you backup this file, as losing it means your Atsign's data becomes unrecoverable!

Activating is also known as "CRAM authentication" or "onboarding."

Activating can be done with the [at\_activate](https://pub.dev/packages/at_onboarding_cli) binary which can be downloaded several ways: from [pub.dev](https://pub.dev/packages/at_onboarding_cli), [NoPorts release archives](https://github.com/atsign-foundation/noports/releases/latest), or from [GitHub source](https://github.com/atsign-foundation/at_client_sdk/tree/trunk/packages/at_onboarding_cli).

## at\_activate

### Installing at\_activate

`at_activate` is Atsign's official activation CLI tool. It contains functions like initial activation (CRAM) and for generating additional namespace-scoped copies (APKAM).

To install it, you must have Dart installed on your system.

1. Ensure you have [Dart](https://dart.dev/get-dart) installed

```
dart --version
```

2. Install [at\_activate](http://pub.dev/packages/at_onboarding_cli) using `dart pub`&#x20;

```
dart pub global activate at_onboarding_cli
```

3. You should be able to run `at_activate` in your terminal

```
at_activate
```

If missing, check `$HOME/.pub-cache/bin` for the at\_activate binary.

### Uninstalling at\_activate

Simply use `dart pub global deactivate`

```
dart pub global deactivate at_onboarding_cli
```

### Initial activation

Ensure you have `at_activate` installed before moving on. Check out [#installing-at\_activate](#installing-at_activate "mention") on how to do that.

1. Use the `onboard` command to onboard your Atsign. Replace `<@atsign>` with your Atsign. Example: `at_activate onboard -a @alice`

```
at_activate onboard -a <@atsign>
```

2. You will then be prompted with a yes/no question. It is very important you backup and keep this file safe, once it is generated in the next steps. Enter "Y" and continue.

<figure><img src="/files/oRVsAzk57FKl4KUFWK5X" alt=""><figcaption></figcaption></figure>

3. You will receive an OTP in your email. Enter the OTP into the terminal.

```
[Information] Requesting my.atsign.com to send a verification code
[Information] Successfully sent verification code to your registered e-mail or phone
[Action Required] Enter your verification code: DTBW
```

4. Your Atsign will then be activated and manager keys will be generated in `$HOME/.atsign/keys`

```
[Information] Fetching CRAM Key from my.atsign.com
[Information] CRAM Key fetched successfully

      Find : #[1/50] : Found atServer address for @blueshark82_jttest in atDirectory - 2b8169fc-de0b-5a90-90cb-ed600904f4f0.swarm0003.atsign.zo...
   Connect : #[2/50] : Connected to @blueshark82_jttest atServer
[Success] Your .atKeys file saved at /home/user/.atsign/keys/@blueshark82_jttest_key.atKeys
```

5. Backup a copy of this file in some place safe (like a personal drive or a secure cloud storage).

### Enrolling new devices

{% hint style="info" %}
This is also known as creating an "APKAM copy" of your keys.
{% endhint %}

Now that your Atsign is activated, we can administer a new copy of the .atKeys file with a namespace-restriction.&#x20;

A namespace-restriction means that this new .atKeys file copy will only have read and/or write access to certain namespaces. Read more on namespaces [here](/atsign-platform/atrecord#visibility-scope).

The following steps are typically done on two separate devices, but is still possible to do on one. We will refer to **Device 1** as the device with the manager set of keys and **Device 2** as the device who wishes to enroll under this Atsign.&#x20;

| Device   | Purpose                                                           |
| -------- | ----------------------------------------------------------------- |
| Device 1 | The device with the manager .atKeys file                          |
| Device 2 | The enrolling device that will generate the new .atKeys file copy |

Before beginning, you must establish a few strings and keep this in mind.

| Variable    | Purpose                                    | Example          |
| ----------- | ------------------------------------------ | ---------------- |
| App Name    | App namespace that this key will belong to | my\_app          |
| Device Name | The name of the enrolling device           | linux\_server\_1 |

**Device 1** will generate an OTP and create an auto approval process, then **Device 2** will send the enrollment request and this will automatically generate an APKAM .atKeys file copy.

1. On **Device 1**, generate an OTP. Take note of this OTP, as it will be needed in step 3.&#x20;

Replace `<@atsign>` with the Atsign you are making a copy of.

```
at_activate otp -a <@atsign>
```

**Tip:** you can make this OTP useable for longer by setting an expiry by appending `--expiry 2h`  to the command above.

2. On **Device 1**, create an auto approval service.&#x20;

| Parameter       | Description                                                                                 | Example           |
| --------------- | ------------------------------------------------------------------------------------------- | ----------------- |
| `<@atsign>`     | The Atsign you are making a copy of                                                         | `@alice`          |
| `<app_name>`    | Name of the application/use case. Think of this as the purpose you are making this key for. | `noports`         |
| `<device_name>` | Device name that uniquely identifies this enrollment from other enrollments.                | `linux_server_01` |

```
at_activate auto \
  -L 1 \
  -a <@atsign> \
  --arx <app_name> \
  --drx <device_name>
```

This will set up an auto service with a limit of 1. Leave this process running in the background.

3. On **Device 2,** send the enrollment request.

| Parameter            | Description                                                                                                                                                                             | Example                                                                                                       |
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `<@atsign>`          | Atsign you are making a copy of                                                                                                                                                         | `@alice`                                                                                                      |
| `<app_name>`         | The same app name from Step 2                                                                                                                                                           | `noports`                                                                                                     |
| `<device_name>`      | The same device name from Step 1                                                                                                                                                        | `linux_server_01`                                                                                             |
| `<[namespace:r?w?]>` | <p>A list of comma-separated namespace and their read/write permissions.<br><br>If you want this copy to simply be a revokable copy with all access, set this to <code>\*:rw</code></p> | <p><code>"\*:rw"</code></p><p></p><p></p><p></p><p><code>"sshnp:rw,sshrvd:rw,noports:r,at\_talk:w"</code></p> |
| `<OTP>`              | The one-time passcode from Step 1                                                                                                                                                       | `ABC123`                                                                                                      |

Change all parameters. Note that you have to replace `<@atsign>` twice in this command (`-a` and `--keys` ).

```
at_activate enroll \
  -a <@atsign> \
  --keys ~/.atsign/keys/<@atsign>_key.atKeys \
  --app <app_name> \
  --device <device_name> \
  -n "<[namespace:r?w?]>" \
  -s <OTP>
```

If you have set up an auto approval service up correctly (which is running in the background from Step 2), then executing this enroll command should exit successfully after a couple of seconds.

Example command & output:

```
❯ at_activate enroll \
    -a @alice \
    --keys ~/.atsign/keys/@alice_key.atKeys \
    --app test_app \
    --device copy1 \
    -n "*:rw" \
    -s DWB1HY

    Enroll : submitting enrollment requestEnrollment ID: 7dec3458-a939-4296-82ea-a6e042e72f52
Waiting for approval; will check every 10 seconds
    Enroll : submitted OK
      PKAM : Enrollment has been approved (PKAM auth success)Creating atKeys file
[Success] Your .atKeys file saved at /home/user/.atsign/keys/@alice_key.atKeys
```

## Exceptions

### AT0032 - Exception

This is a common exception that many people run into.

Your error message will say that this enrollment is in an `approved`  state or a `pending` state.&#x20;

To fix the pending error, go to [#pending-state](#pending-state "mention").&#x20;

To fix the approved error, go to [#approved-state](#approved-state "mention").

#### Pending State

You most likely cancelled the enrollment process or missed a step during your [Enrolling new device](#enrolling-new-devices) handshake.

To fix this, go to **Device 1** (the device with the manager .atKeys file) and we will have to **deny** and **delete** this enrollment request. If your enrollment error says it's in a `pending` state, then you will need to run these two operations: `deny`  and `delete`.

1. Go to **Device 1** (the device with the manager .atKeys file).
2. Confirm your error message is similar to below (says "`in pending state` ")

```
    Enroll : submitting enrollment request

ERROR: enroll : ErrorCode: AT0032 - Exception: Exception: Another enrollment with id 460d24b9-9194-4718-bef2-96f7f467b04b exists with the app name: test_app and device name: copy2 in pending state

Please try again or contact support@atsign.com
```

3. Run the `list` command and copy the enrollment ID of the faulty enrollment request.

```
at_activate list -a <@atsign>
```

Example:

```
❯ at_activate list -a @alice
Connecting ... Connected
Found 3 matching enrollment records
Enrollment ID                         Status    AppName             DeviceName                            Namespaces
460d24b9-9194-4718-bef2-96f7f467b04b  pending   test_app            copy2                                 {*: rw}                        {*: rw}
a1de4e17-0eac-4860-a303-55a9c66358a8  approved  firstApp            firstDevice                           {__manage: rw, *: rw}
```

It is very important we leave the **firstDevice** enrollment untouched.

In this scenario, I will copy the enrollment ID `460d24b9-9194-4718-bef2-96f7f467b04b` .

4. Run the `deny` command.

```
at_activate deny -a <@atsign> -i <enrollment_id>
```

Example:

```
❯ at_activate deny -a @alice -i 460d24b9-9194-4718-bef2-96f7f467b04b
Connecting ... Connected
Denying enrollmentId 460d24b9-9194-4718-bef2-96f7f467b04b
Server response: data:{"status":"denied","enrollmentId":"460d24b9-9194-4718-bef2-96f7f467b04b"}
```

5. Run the `delete` command.

```
at_activate delete -a <@atsign> -i <enrollment_id>
```

Example:

```
❯ at_activate delete -a @alice -i 460d24b9-9194-4718-bef2-96f7f467b04b
Connecting ... Connected
Sending delete request
Server response: {"enrollmentId":"460d24b9-9194-4718-bef2-96f7f467b04b","status":"deleted"}
```

6. Follow steps 1-3 again in [Enrolling new devices](#enrolling-new-devices) and that should resolve the error from coming up again!

#### Approved State

You are most likely trying to re-enroll an Atsign that has been previously enrolled on another device, and you would like to now re-enroll on a new completely separate device.

To fix this, go to **Device 1** (the device with the manager .atKeys file) and we will need to `revoke` and `delete`.

1. Go to **Device 1** (the device with the manager .atKeys file)
2. Confirm that error message you got on **Device 2** is similar to below (it says "`in approved state`")

```
    Enroll : submitting enrollment request

ERROR: enroll : ErrorCode: AT0032 - Exception: Exception: Another enrollment with id 7dec3458-a939-4296-82ea-a6e042e72f52 exists with the app name: test_app and device name: copy1 in approved state

Please try again or contact support@atsign.com
```

3. Run the `list` command and copy the enrollment ID of the faulty enrollment request.

```
at_activate list -a <@atsign>
```

Example:

```
❯ at_activate list -a @alice
Connecting ... Connected
Found 3 matching enrollment records
Enrollment ID                         Status    AppName             DeviceName                            Namespaces                         {*: rw}
7dec3458-a939-4296-82ea-a6e042e72f52  approved  test_app            copy1                                 {*: rw}
a1de4e17-0eac-4860-a303-55a9c66358a8  approved  firstApp            firstDevice                           {__manage: rw, *: rw}
```

It is very important we leave the **firstDevice** enrollment untouched.

In this scenario, I will copy the enrollment ID `7dec3458-a939-4296-82ea-a6e042e72f52`.

4. Run the `revoke` command.

```
at_activate revoke -a <@atsign> -i <enrollment_id>
```

Example:

```
❯ at_activate revoke -a @alice -i 7dec3458-a939-4296-82ea-a6e042e72f52
Connecting ... Connected
Revoking enrollmentId 7dec3458-a939-4296-82ea-a6e042e72f52
Server response: data:{"status":"revoked","enrollmentId":"7dec3458-a939-4296-82ea-a6e042e72f52"
```

5. Run the `delete` command.

```
at_activate delete -a <@atsign> -i <enrollment_id>
```

Example:

```
❯ at_activate delete -a @alice -i 7dec3458-a939-4296-82ea-a6e042e72f52
Connecting ... Connected
Sending delete request
Server response: {"enrollmentId":"7dec3458-a939-4296-82ea-a6e042e72f52","status":"deleted"}
```

6. Follow steps 1-3 again in [Enrolling new devices](#enrolling-new-devices) and that should resolve the error from coming up again!

## Help! I've lost my .atKeys

Atsign cannot recover your lost cryptographic keys.

The only path forward is to reset your atServer which will result in all data being wiped. However, your Atsign (handle) will still be usable.

To reset your Atsign, go to [https://my.atsign.com](https://my.atsign.com/), login with the email address that is tied to that Atsign, then navigate to the manage page of your Atsign and open the Reset dropdown. Type the Atsign to proceed with resetting your atServer.

<figure><img src="/files/1nkGxBSQBDVjpAgPpz8H" alt=""><figcaption></figcaption></figure>

You can also reach out to `support@atsign.com` and we can help you reset your Atsign. Please reach out with the corresponding email that is tied to the relevant Atsign.


# atRecord

The format atServers use to store and share data.

{% hint style="info" %}
Whenever we mention the word "key" we are normally talking about a cryptographic key.

The main exception being the atKey, this is the "key" of a "key value pair" that makes up every atRecord.&#x20;

It's unfortunate that the word "key" is polymorphic in computer science. We have tried in the past to move to other words, but we have decided to stick with "keys," and the developer will have to understand the context—whether it is a cryptographic key or key value pair (sorry!).
{% endhint %}

atRecords are the data records that are stored by the atServers. We use the common key-value pair format.&#x20;

## atKey

An atKey is the identifier half of the "key-value" pair. Similar to the primary key of a tabular database, the atKey must be a unique string which represents the data.

There are 5 different types of an atKey.

<table><thead><tr><th width="119.33333333333331">Type</th><th>Purpose</th></tr></thead><tbody><tr><td>Public</td><td>Store and share public data which can be seen by anyone.</td></tr><tr><td>Self</td><td>Store data which can only be seen by the owner.</td></tr><tr><td>Shared</td><td>Store and share private data which can only be seen by the owner and intended recipient.</td></tr><tr><td>Private</td><td>Store data which can only be seen by the owner, hidden by default.</td></tr><tr><td>Cached</td><td>Cache shared data from other atSigns for performance and offline mode.</td></tr></tbody></table>

### Structure of an atKey

```
[cached:]<visibility scope>:<record ID><owner’s atSign>
```

#### Cached

A marker for whether it is a cached key or not.

#### Visibility Scope

A marker for who has access to the key.

#### Record ID

A unique string used to represent the atRecord.

#### Owner's atSign

The owner (i.e. creator's) Atsign for that particular atRecord. The shared by Atsign of an atRecord is synonymous to the owner Atsign of an atRecord.

<details>

<summary>Rules for atKeys</summary>

1. Length of an atKey should not be more than 240 characters\
   (a limitation of the current implementation of the atServer, not a protocol limitation)
2. A maximum of 55 7-bit characters for the atSign (unicode is translated to UTF-7)
3. Allowed characters in an entity are: `[\w._,-"']`
4. Namespace is mandatory in the current implementation of the protocol\
   i.e entity must follow the notation: `<identifier>.<namespace>`
5. Cached atKeys should have a different owner than the current Atsign
6. Visibility scope and owner cannot be the same for a shared atKey
7. Reserved atKeys cannot be [modified](/atsdk/sdk/crud-operations) or [notified](/atsdk/sdk/events)
8. For newly created atKeys, the owner must match the current Atsign

</details>

<details>

<summary>Reserved atKeys</summary>

The following is a list of reserved atIKeys which the atServer requires to function.

**Don't** try to delete or overwrite these keys, the atServer cannot function without them.

* `privatekey:at_pkam_privatekey`
* `privatekey:at_pkam_publickey`
* `public:publickey`
* `privatekey:privatekey`
* `shared_key`
* `privatekey:self_encryption_key`
* `signing_privatekey`
* `public:signing_publickey`
* `privatekey:at_secret`
* `privatekey:at_secret_deleted`

</details>

<details>

<summary>Example atKeys</summary>

**Public atKey**

1. A `public` atKey with a record id of `location` shared by `@alice`. This atKey typically holds public data that any Atsign can access.

`public:location@alice`

2. A `public` atKey with a record id of `publickey` shared by `@bob`. Note that this is a [reserved atKey](#reserved-atids).

`public:publickey@bob`

**Private atKey**

1. A `private` atKey with a record id of `pk1` shared by `@alice`.

`privatekey:pk1@alice`

**Shared atKey**

1. A `shared` atKey with a record id of `phone` shared with `@bob`, shared by `@alice`.

`@bob:phone@alice`

2. A `shared` atKey with the record id of `name`, a namespace of `wavi`, shared with `@alice`, and shared by `@bob`.

`@alice:name.wavi@bob`

**Internal atKey**

1. An `internal` atKey with a record id of `_latestnotificationid`, namespace of `at_skeleton_app` and is shared by `@alice`.

`_latestnotificationid.at_skeleton_app@alice`

**Cached atKey**

1. A `cached` atKey with a record id of `phone`, shared with `@bob`, and is shared with `@alice`.

`cached:@bob:phone@alice`

</details>

## atMetadata

Metadata of the atRecord is also stored and describes the following properties of the atValue.

<table data-header-hidden><thead><tr><th width="174.33333333333331"></th><th width="144"></th><th></th></tr></thead><tbody><tr><td><strong>Meta Attribute</strong></td><td><strong>Auto create?</strong></td><td><strong>Description</strong></td></tr><tr><td>availableFrom</td><td>Yes</td><td>A Date and Time derived from the ttb (now + ttb). A Key should be only available after availableFrom.</td></tr><tr><td>ccd</td><td>No</td><td>Indicates if a cached key needs to be deleted when the Atsign owner who has originally shared it deletes it.</td></tr><tr><td>createdBy</td><td>Yes</td><td>Atsign that has created the key</td></tr><tr><td>createdOn</td><td>Yes</td><td>Date and time when the key was  created.</td></tr><tr><td>expiresOn</td><td>Yes</td><td>A Date and Time derived from the ttl (now + ttl). A Key should be auto deleted once it expires.</td></tr><tr><td>isBinary</td><td>No</td><td>True if the value is a binary value.</td></tr><tr><td>isCached</td><td>No</td><td>True if the key is cached.</td></tr><tr><td>isEncrypted</td><td>No</td><td>True if the value is encrypted.</td></tr><tr><td>refreshAt</td><td>No</td><td>A Date and Time derived from the ttr. The time at which the key gets refreshed.</td></tr><tr><td>sharedWith</td><td>No</td><td>Atsign of the individual with whom the key has been shared. Can be null if not shared with anyone.</td></tr><tr><td>updatedOn</td><td>Yes</td><td>Date and time when the key was  last updated.</td></tr><tr><td>ttb</td><td>No</td><td>Time to birth in milliseconds.</td></tr><tr><td>ttl</td><td>No</td><td>Time to live in milliseconds.</td></tr><tr><td>ttr</td><td>No</td><td>Time in milliseconds after which the cached key needs to be refreshed. A ttr of -1 indicates that the key can be cached forever. ttr of 0 indicates do not refresh. ttr of > 0 will refresh the key. ttr of null indicates the key is impossible to cache, hence, refreshing does not make sense (which has the same effect as a ttr of 0).</td></tr></tbody></table>

## atValue

You can save text or binary values in an atServer.&#x20;

**While the atServer is suitable for small objects, you should handle large objects by reference.**

For example, to share a large file:<br>

1. Derive a new encryption key.
2. Encrypt the file.
3. Upload the file to a storage location.
4. Notify other Atsigns of the location and the encryption key.

This "by refrence" pattern is used in applications like [NoPorts](https://noports.com) to ensure efficient data transfer.&#x20;

{% hint style="warning" %}
The size of the value saved in an atServer is bound by the atPlatform Protocol's config parameter "maxBufferSize".
{% endhint %}


# Infrastructure

How we scale and provide resilience.

The Atsign Platform is designed to be distributed and allows people to run their own infrastructure for atDirectory and atServer services on their own networks. Here, we show how at a high level how Atsign runs the Internet Atsign Platform Infrastructure.

Atsign services are monitored by an independent third party for uptime and can be seen here:

{% embed url="<https://status.atsign.com/>" %}
UpTimeRobot Reports
{% endembed %}

## atDirectory

Atsign runs the Internet atDirectory, which has to be resilient and dependable. To provide that level of service, we use Google's Cloud Platform, Kubernetes, containers, and a distributed in-memory database.&#x20;

<figure><picture><source srcset="/files/LUnVuvRa1AXPttaWOfup" media="(prefers-color-scheme: dark)"><img src="/files/v1j9KMDsGpfc9JZfE9Eu" alt="Architecture Diagram of the atDirectory infrastructure"></picture><figcaption><p>Highly available design</p></figcaption></figure>

The atDirectory runs in a GCP Virtual Private Cloud. This VPC also houses an auto-scaling Kubernetes cluster which is spread across multiple datacenters and availability zones.

The atDirectory service is found on the well-known DNS address `root.atsign.org` on port `64.` This is load balanced across the atDirectory containers. These containers, through an internal load balancer, to read-only in-memory databases containing the Atsign to Fully Qualified Domain Name (FQDN) and port number mappings for all Atsigns.

The read-only databases are kept up to date with a single read-write database. This database is updated by the registrar [website](https://my.atsign.com/), which is run in another Kubernetes cluster.

This design has proved to be reliable and allows upgrades in place without downtime. It automatically scales as load increases by spinning up more containers, and, if required, by adding new machines to the cluster itself. GCP's platform and Kubernetes have demonstrated resilience during data center or hardware issues, and have self-healed the infrastructure.

## atServers

Each Atsign has its own dedicated personal data store, called an "atServer," running as a Docker container within a Docker Swarm. We run a number of Docker Swarms and can move atServers from one swarm to another. However, for high availability, we rely on the Docker Swarm's manager nodes to orchestrate and ensure each atServer is up and running even if hardware fails within a swarm.

Why Docker Swarm and not Kubernetes? Kubernetes is an excellent choice for groups of containers that provide a service like the atDirectory or websites. But, Kubernetes does not scale down well for thousands or millions of tiny independent containers like atServers.  Docker Swarm also provides highly resilient networking and is very lightweight.

<figure><picture><source srcset="/files/8aamF5Rvmx59b4OeaeW2" media="(prefers-color-scheme: dark)"><img src="/files/FcgpLAOao9ZpVPyn8xc9" alt=""></picture><figcaption><p>resilient atServer Cluster</p></figcaption></figure>

The FQDN and port number for a given Atsign from the atDirectory is connected to the Docker Swarm. Each Docker Swarm node will route the port number to the right container on the swarm via its internal VXLAN. The Manager Nodes are responsible for ensuring each container is running and available across the whole swarm.

For data requiring persistent storage beyond the Docker Swarm, encrypted atServer data gets transferred to a highly resilient NetApp Cloud Volume managed by GCP. This cloud volume functions as a network file system accessible to the atServers. &#x20;

All infrastructure components are distributed across multiple data centers and availability zones, and have proven to be highly reliable with very little downtime of individual atServers during failures or upgrades.


# atSDK Overview

Get to know Atsign's SDK, the atSDK

The atSDK is the best way to embed the Atsign Protocol into new or existing software. This can be anything from a graphical desktop application to firmware flashed on a microcontroller.

### Sections

<table data-column-title-hidden data-view="cards"><thead><tr><th data-card-target data-type="content-ref">Link</th><th>Description</th></tr></thead><tbody><tr><td><a href="/pages/29lRmzORjJjQ0SECfv58">/pages/29lRmzORjJjQ0SECfv58</a></td><td>Setup the atSDK for your preferred language</td></tr><tr><td><a href="/pages/r5WrMBaiaC2Nz0nFbPpu">/pages/r5WrMBaiaC2Nz0nFbPpu</a></td><td>How to authenticate to an atServer</td></tr><tr><td><a href="/pages/btfHrhsuHwgxTLZpajTY">/pages/btfHrhsuHwgxTLZpajTY</a></td><td>Learn how to create atKeys for your chosen platform</td></tr><tr><td><a href="/pages/Ej3Lr2XTD4miZiFNE5Id">/pages/Ej3Lr2XTD4miZiFNE5Id</a></td><td>How to do basic CRUD operations on an atServer</td></tr><tr><td><a href="/pages/nzMHbToyp0fQoBJds83e">/pages/nzMHbToyp0fQoBJds83e</a></td><td>How to send and receive real-time messages</td></tr><tr><td><a href="/pages/xM9GfVjyA4FjqkwvbgMI">/pages/xM9GfVjyA4FjqkwvbgMI</a></td><td>Implementation specific features to know about</td></tr></tbody></table>


# Get Started

Set up the atSDK for your preferred language

{% tabs %}
{% tab title="Dart" %}

## Starter Dart App

Guide coming soon!

For now, please check out our [Dart atSDK Walkthrough](/atsdk/atsdk-walkthroughs/atsdk-tutorial)
{% endtab %}

{% tab title="Flutter" %}

## Starter Flutter App

*Create a Flutter application initialized for use with the atSDK*

{% hint style="warning" %}
If you don't have a Flutter setup for development yet, please see the [Flutter docs](https://docs.flutter.dev/get-started).
{% endhint %}

**1. Create a new Flutter app**

First, generate a new Flutter app (replace `at_app` with the name of your application).

```sh
flutter create at_app
cd at_app
```

#### 2. Add dependencies

Then add the following dependencies to your project:

```sh
flutter pub add at_client_mobile at_onboarding_flutter flutter_dotenv path_provider
```

#### 3. Replace main.dart

Now copy the contents of `main.dart` from below into `lib/main.dart` into your project.

<details>

<summary>main.dart</summary>

```dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:at_onboarding_flutter/at_onboarding_flutter.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:path_provider/path_provider.dart'
    show getApplicationSupportDirectory;

import 'home_screen.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await dotenv.load();
  runApp(const MyApp());
}

Future<AtClientPreference> loadAtClientPreference() async {
  var dir = await getApplicationSupportDirectory();

  return AtClientPreference()
    ..rootDomain = 'root.atsign.org'
    ..namespace = dotenv.env['NAMESPACE']
    ..hiveStoragePath = dir.path
    ..commitLogPath = dir.path
    ..isLocalStoreRequired = true;
  // * By default, this configuration is suitable for most applications
  // * In advanced cases you may need to modify [AtClientPreference]
  // * Read more here: https://pub.dev/documentation/at_client/latest/at_client/AtClientPreference-class.html
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  // * load the AtClientPreference in the background
  Future<AtClientPreference> futurePreference = loadAtClientPreference();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // * The onboarding screen (first screen)
      home: Scaffold(
        appBar: AppBar(
          title: const Text('MyApp'),
        ),
        body: Builder(
          builder: (context) => Center(
            child: ElevatedButton(
              onPressed: () async {
                AtOnboardingResult onboardingResult =
                    await AtOnboarding.onboard(
                  context: context,
                  config: AtOnboardingConfig(
                    atClientPreference: await futurePreference,
                    rootEnvironment: RootEnvironment.Production,
                  ),
                );
                if (mounted) {
                  switch (onboardingResult.status) {
                    case AtOnboardingResultStatus.success:
                      Navigator.push(
                        context,
                        MaterialPageRoute(builder: (_) => const HomeScreen()),
                      );
                      break;
                    case AtOnboardingResultStatus.error:
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(
                          backgroundColor: Colors.red,
                          content: Text('An error has occurred'),
                        ),
                      );
                      break;
                    case AtOnboardingResultStatus.cancel:
                      break;
                  }
                }
              },
              child: const Text('Onboard an @sign'),
            ),
          ),
        ),
      ),
    );
  }
}

```

</details>

#### 4. Add home\_screen.dart

Create a second file in `lib/` called `home_screen.dart`. Copy the contents of `home_screen.dart` from below into your project.

<details>

<summary>home_screen.dart</summary>

```dart
import 'package:at_client_mobile/at_client_mobile.dart';
import 'package:flutter/material.dart';

// * Once the onboarding process is completed you will be taken to this screen
class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // * Getting the AtClientManager instance to use below
    AtClientManager atClientManager = AtClientManager.getInstance();
    // * Getting the AtClient instance to use below
    AtClient atClient = atClientManager.atClient;

    // * From this widget onwards, you are fully onboarded!
    // * You can use the AtClient instance to perform operations as the onboared atSign
    return Scaffold(
      appBar: AppBar(
        title: const Text('What\'s my current @sign?'),
      ),
      body: Center(
        child: Column(children: [
          const Text('Successfully onboarded and navigated to FirstAppScreen'),

          // * Use the AtClient to get the current @sign
          Text('Current @sign: ${atClient.getCurrentAtSign()}')
        ]),
      ),
    );
  }
}

```

</details>

#### 5**a. gitignore the .env file**

Add the following line to the top of `.gitignore`:

```
.env
```

This will prevent accidentally uploading the secret values stored in `.env` to any git repositories associated with your app.

**5b. Add and populate the .env file**

Create a new file in the root of the project (next to `pubspec.yaml`) called `.env`. Add the following lines to it:

```sh
NAMESPACE=
API_KEY=
```

These values should be populated with your unique app namespace, and registrar API key.

**5.c. Register the .env file in pubspec.yaml**

In `pubspec.yaml` there should be a `flutter:` key, under this key, we want to create and add `.env` the `assets:` list:

```yaml
...
flutter:
  ...
  assets:
    - .env
  ...
...
```

#### 6. Start developing!

Start your application with the following command:

```sh
flutter run
```

Now you are ready to begin developing!
{% endtab %}

{% tab title="C" %}

## Get Started C Application

Setup a CMake project which includes the atSDK package suite.

The full example can be found [here](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c/0-my-first-c-app).

#### 1. Ensure you have a C Compiler, CMake, and a build automation tool

The method at which you decide to use to install these prerequisites are up to you. These three tools can be installed using either `apt` or `pip`.

Firstly, ensure you have a C compiler such as [clang](https://clang.llvm.org/) or [gcc](https://gcc.gnu.org/). We recommend [gcc](https://gcc.gnu.org/) because that is currently what Atsign is focused on supporting.

```bash
gcc --version
```

Secondly, ensure you have [CMake](https://cmake.org/) installed using `cmake --version`. Ensure that your CMake version is >= 3.24. If you have issues obtaining a newer version of CMake, we have found lots of success using `pip` when installing CMake via `pip install cmake@3.24`

```bash
cmake --version
```

Lastly, ensure you have a automation tool installed, such as [GNU Make](https://www.gnu.org/software/make/). We recommend [GNU Make](https://www.gnu.org/software/make/) because that is currently what Atsign is focused on supporting. These examples are using GNU Make version 4.3, but any version should work.

```bash
make --version
```

#### 2. Create an empty directory

This command will create a directory and then go into it.

```sh
mkdir my_first_c_app && cd my_first_c_app
```

#### 3. Create CMakeLists.txt

Inside your project folder, create a new file called `CMakeLists.txt.`

Copy and paste the CMake code into the file. Feel free to change the project name by changing this line:`project(my_first_c_app)` .

```cmake
# Minimum cmake version required by atSDK is 3.24
cmake_minimum_required(VERSION 3.24)

project(my_first_c_app)

include(FetchContent)

FetchContent_Declare(
  atsdk
  URL https://github.com/atsign-foundation/at_c/archive/refs/tags/v0.1.0.tar.gz
  URL_HASH SHA256=7ca4215a473037ca07bef362b852291b0a1cf4e975d24d373d58ae9c1df832bc
)

FetchContent_MakeAvailable(atsdk)

add_executable(main ${CMAKE_CURRENT_LIST_DIR}/main.c)

target_link_libraries(main PRIVATE atclient)
```

The above `CMakeLists.txt` will use FetchContent to download the C atSDK for you. Then, we will create a target named `main` for you and link our `atclient` library statically.&#x20;

#### 4. Create main.c

Inside your project folder, create a new file called `main.c`

Change the line `#define ATSIGN "@jeremy_0"` to the Atsign that you have keys to. For example, if you own an Atsign `@alice` and have its keys in the correct directory `~/.atsign/keys/@alice_key.atKeys`, then I would change this line in my code to `#define ATSIGN "@alice"`

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@jeremy_0"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient;
    atclient_init(&atclient);

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }
    
    if ((exit_code = atclient_pkam_authenticate(&atclient, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    atlogger_log("my_first_c_app", ATLOGGER_LOGGING_LEVEL_INFO, "Authenticated to atServer successfully!\n");

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient);
    return exit_code;
}
}
```

#### 5. CMake Configure

In the terminal, run the following command.

```bash
cmake -S . -B build
```

This is known as the "configure" step where CMake will build instructions on how to build the app specific to your machine.

Output should be similar to the following:

```bash
$ cmake -S . -B build
-- [ATSDK] ATSDK_AS_SUBPROJECT: ON
-- Building atlogger
-- [ATLOGGER] ATLOGGER_AS_SUBPROJECT: ON
-- Building atchops
-- [ATCHOPS] ATCHOPS_AS_SUBPROJECT: ON
-- [MbedTLS] fetching package...
-- [uuid4] fetching package...
-- Building atclient
-- [ATCLIENT] ATCLIENT_AS_SUBPROJECT: ON
-- [cjson] fetching package...
-- Configuring done
-- Generating done
-- Build files have been written to: /home/jeremy/GitHub/at_demos/demos/get_started_c/1-authentication/build
```

#### 6. Build

Use CMake to build your configure and build your programs:

```bash
cmake --build build
```

This command is the same as running `cd build && make all` (if you are using Makefiles).&#x20;

Your output will look similar to:

```
cmake --build build
[ 50%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main
```

#### 7. Run the binary

The previous command, if ran successfully, would have created a binary executable for you: `./build/main`.

Simply run the program:

```bash
./build/main
```

Your output will look similar to:

```bash
[DEBG] 2024-08-15 00:37:16.517243 | connection |        SENT: "jeremy_0"
[DEBG] 2024-08-15 00:37:16.550027 | connection |        RECV: "3b419d7a-2fee-5080-9289-f0e1853abb47.swarm0002.atsign.zone:5770"
[DEBG] 2024-08-15 00:37:16.892252 | connection |        SENT: "from:jeremy_0"
[DEBG] 2024-08-15 00:37:16.929632 | connection |        RECV: "data:_d6d1a569-b944-4fd1-8c1e-7fc26e508452@jeremy_0:75be9182-bfdc-437f-96bc-c45eb5a1c0ff"
[DEBG] 2024-08-15 00:37:16.968986 | connection |        SENT: "pkam:QLuCl8JITCbOF6J8A6WlqBX3FUKoRR8DHh0FMnbVhHYRiuXZdBHJks0aQtq2cUj47KdhJCvN3Uk728UJHzwLgIF00wE3893xUi+9luoD7OFdV4Pbtmrxvj4K/s81A4Z9XBEJwtKerWStUavX5hR39By6Y6NJ2HeCuqVvBw2WQ/gKM15cAndpXrYzEVNJk9eCCN4+VXWxJOm6FhoY41Qxn/QSYqnRqp6PfHiY1rFXjsikJX6ip3LrCFTabJAS78BHx4BmmUzGKVPn0dFtYwvEKBgumViQhKHwS1ae1xBkLTZCARdIUhdhudoyba/ogwIEMjTWanLzAbqtNRa6qPTDrw=="
[DEBG] 2024-08-15 00:37:17.018977 | connection |        RECV: "data:success"
[INFO] 2024-08-15 00:37:17.019891 | my_first_c_app | Authenticated to atServer successfully!
```

Congratulations! You have successfully run a barebones Atsign C application.
{% endtab %}
{% endtabs %}


# Authentication

How to authenticate to an atServer

{% tabs %}
{% tab title="Dart" %}

## Dart

### **Package Installation**

In Dart we provide the[ at\_onboarding\_cli](https://pub.dev/packages/at_onboarding_cli) package which handles onboarding to the atServer via files stored in the \~/.atsign/keys directory

Add the package to your project automatically using pub:

```
dart pub add at_onboarding_cli
```

### Usage

Set up the [preferences](https://pub.dev/documentation/at_onboarding_cli/latest/at_onboarding_cli/AtOnboardingPreference-class.html) to onboard to the atServer.&#x20;

```dart
 AtOnboardingPreference atOnboardingConfig = AtOnboardingPreference()
    ..hiveStoragePath = '$homeDirectory/.$nameSpace/$fromAtsign/storage'
    ..namespace = nameSpace
    ..downloadPath = '$homeDirectory/.$nameSpace/files'
    ..isLocalStoreRequired = true
    ..commitLogPath = '$homeDirectory/.$nameSpace/$fromAtsign/storage/commitLog'
    ..rootDomain = rootDomain
    ..fetchOfflineNotifications = true
    ..atKeysFilePath = atsignFile
    ..atProtocolEmitted = Version(2, 0, 0);
```

Next, get the onboardingService.&#x20;

```dart
  AtOnboardingService onboardingService = AtOnboardingServiceImpl(
      fromAtsign, atOnboardingConfig,
      atServiceFactory: atServiceFactory);
```

Finally, wait to be onboarded. This returns true once complete.&#x20;

```dart
await onboardingService.authenticate();
```

This can be wrapped to check that the onboard was successful with the code snippet below.

```dart

bool onboarded = false;
  Duration retryDuration = Duration(seconds: 3);
  while (!onboarded) {
    try {
      stdout.write('\r\x1b[KConnecting ... ');
      await Future.delayed(Duration(
          milliseconds:
          1000)); // Pause just long enough for the retry to be visible
      onboarded = await onboardingService.authenticate();
    } catch (exception) {
      stdout.write(
          '$exception. Will retry in ${retryDuration.inSeconds} seconds');
    }
    if (!onboarded) {
      await Future.delayed(retryDuration);
    }
  }
  stdout.writeln('Connected');
```

{% endtab %}

{% tab title="Flutter" %}

## Flutter

{% hint style="warning" %}
If you followed the [Get Started](/atsdk/sdk/get-started) guide for Flutter, then you should already have onboarding setup in your app. Feel free to skip this section and move on.
{% endhint %}

### Package Installation

In Flutter, we provide the [at\_onboarding\_flutter](https://pub.dev/packages/at_onboarding_flutter) package which handles secure management of these secret keys.

Add the package to your project automatically using pub:

```
flutter pub add at_onboarding_flutter
```

### Usage

Simply call the [`onboard`](https://pub.dev/documentation/at_onboarding_flutter/latest/at_onboarding/AtOnboarding/onboard.html) method whenever you want your app to open the onboarding widget.

```dart
AtOnboardingResult onboardingResult = await AtOnboarding.onboard(
  context: context,
  config: AtOnboardingConfig(
    atClientPreference: futurePreference,
    rootEnvironment: RootEnvironment.Production,
    appAPIKey: dotenv.env['API_KEY'],
    domain: 'root.atsign.org',
  ),
);
```

#### API Key setup

To get an API key for your app, first head to the [registrar website](https://my.atsign.com/dashboard), then click manage under one of your Atsigns:

<figure><img src="/files/Tfz1h3vEKV1ynnEhN9iM" alt=""><figcaption></figcaption></figure>

Then open the Advanced settings drop down and click `Generate New API Key`:

<figure><img src="/files/fRhmUOXPkDCdVHT9uDnb" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="C" %}

## C

Find the full example on our [GitHub](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c/1-authentication#1---authentication).

### 1. Fetch your atServer's address from the production atDirectory

First, include `atclient_utils.h` and `#include <atclient/constants.h>` at the top of you program.

```c
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
```

Next, initialize variables that will **hold** the host and port of our atServer. We will pass pointers to these variables to the function that will populate these values for us.

```c
char *atserver_host = NULL;
int atserver_port = 0;
```

Lastly, use the `atclient_utils_find_atserver_address` function to populate our `atserver_host` and `atserver_port` variables. This function returns an `int` for error handling. A non-zero exit code indicates an error.

```c
if(atclient_utils_find_atserver_address(
  ATCLIENT_ATDIRECTORY_PRODUCTION_HOST,
  ATCLIENT_ATDIRECTORY_PRODUCTION_PORT,
  ATSIGN,
  &atserver_host,
  &atserver_port) != 0) {
  // an error occurred
}
```

Don't forget to free the `atserver_host` variable! It is mentioned in the documentation of the `atclient_utils_find_atserver_address` function that it is the responsibility of the caller to free this variable.

```c
free(atserver_host);
```

### 2. Load your atSign's atKeys

First, include `atkeys.h`.

```c
#include <atclient/atkeys.h>
```

Next, allocate memory for the `atclient_atkeys` struct. In this scenario, we are statically allocating. We need to call the `atclient_atkeys_init` function and pass a pointer to our `atclient_atkeys` struct. You will find this to be a common pattern when working with C structs. This is our method of mimicking object-oriented methodology in our C SDK.

```c
atclient_atkeys atkeys;
atclient_atkeys_init(&atkeys);
```

Finally, call the `atclient_utils_populate_atkeys_from_homedir` function which will look for your `ATSIGN`'s keys in your `$HOME/.atsign/keys/` directory.&#x20;

For example, if my atSign was `@alice`, I would have my `@alice` keys set up such that this file exists: `$HOME/.atsign/keys/@alice_key.atKeys.`

This function returns an `int` for error handling purposes. A non-zero exit code indicates that an error has occurred.

```c
if (atclient_utils_populate_atkeys_from_homedir(
  &atkeys,
  ATSIGN) != 0)
{
    // an error occurred
}
```

Don't forget to run `atclient_atkeys_free`at the end of your application. You will see it as a common pattern that for every `*_init` function that we call, we must have a corresponding `*_free` function.

```c
atclient_atkeys_free(&atkeys);
```

### 3. PKAM Authenticate

If your Atsign's keys exist and your Atsign's atServer is already activated, then this is known as pkam authentication.

First include `atclient.h` at the top of your application.

```c
#include <atclient/atclient.h>
```

Secondly, we will create our `atclient` object. This variable holds data necessary for doing various operations on our atServer later on (such as CRUD or Events).

```c
atclient atclient;
atclient_init(&atclient);
```

Thirdly, call the `atclient_pkam_authenticate` function.&#x20;

This function connects to the atServer (if necessary) and uses your Atsign's atKeys file to generate a special pkam command by signing a challenge sent from the atServer. Once pkam authentication is successful, your connection will be authenticated and your client will be free to make various operations on the atServer.

This function will return an `int` for error handling, in which a non-zero exit code indicates an error.&#x20;

````c
```
if (atclient_pkam_authenticate(
  &atclient,
  atserver_host,
  atserver_port,
  &atkeys,
  ATSIGN) != 0)
{
  // error occurred
}
```
````

Lastly, do not forget to call `atclient_free` at the end of your application.

```c
atclient_free(&atclient);
```

### Example Application

Here is an example application that authenticates my Atsign `@jeremy_0`.

This code is also available on our [GitHub](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c/1-authentication#1---authentication).

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@jeremy_0"

int main()
{
    int exit_code = -1;

    /*
     * this function will print DEBUG logs and below
     */
    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    /*
     * these variables will hold the output of the atclient_utils_find_atserver_address function
     */
    char *atserver_host = NULL;
    int atserver_port = 0;

    /*
     * the `atkeys` variable will hold the encryption keys that the atClient will use to do various things like authentication and end-to-end encryption.
     * these keys are typically located in `~/.atsign/keys/` and would have been generated by you for each Atsign using at_activate.
     * It is important to call the `_init` function before using the struct.
     */
    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    /*
     * the `atclient` variable will hold the state of the atClient and is used to interact with the atServer.
     * you will need to pass this context to most atclient functions
     * It is important to call the `_init` function before using the struct.
     */
    atclient atclient;
    atclient_init(&atclient);

    /*
     * this function will find the atServer's address from the atDirectory
     * and populate the `atserver_host` and `atserver_port` variables
     * with the atServer's address and port.
     * Don't forget to free the `atserver_host` variable after use, when using this function.
     */
    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    /*
     * my keys are assumed to be set up in ~/.atsign/keys/@soccer99_key.atKeys
     * this function will read the keys from the file and populate the `atkeys` variable
     */
    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    /*
     * this function will connect to the atServer, if it is not already connected,
     * then authenticate to the atServer and establish an authenticated connection
     * using the populated `atkeys` variable.
     */
    if ((exit_code = atclient_pkam_authenticate(&atclient, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    atlogger_log("my_first_c_app", ATLOGGER_LOGGING_LEVEL_INFO, "Authenticated to atServer successfully!\n");

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient);
    return exit_code;
}
}
```

{% endtab %}
{% endtabs %}


# atKey Reference

Learn how to create atKeys for your chosen platform

{% hint style="warning" %}
atKey

Please note that any reference to the word "atKey" in this document is not associated with cryptographic keys. The atKey is the "key" of the key-value pair that makes up an atRecord.
{% endhint %}

{% hint style="warning" %}
This article explains how to create an atKey in the  atSDK.

If you are unfamiliar with atKeys please read [this](/atsign-platform/atrecord#atid) first.
{% endhint %}

{% tabs %}
{% tab title="Flutter / Dart" %}

## Flutter / Dart

### Package Installation

The [at\_commons](https://pub.dev/packages/at_commons) package contains common elements used in a number of Atsign's Flutter and Dart packages. This package needs to be included in the application in order to create atKeys.

First add the package to your project:

```
flutter pub add at_commons
```

### Usage

See below for how to create the various types of atKeys.

#### Public atKey

To create a public atKey, first use the `AtKey.public` builder to configure it, then call `.build` to create it.

{% code title="AtKey.public signature" %}

```dart
static PublicKeyBuilder public(String key,
    {String? namespace, String sharedBy = ''})
```

{% endcode %}

The `build` method on `PublicKeyBuilder` takes no parameters.

**Example**

`public:phone.wavi@alice`

{% code overflow="wrap" %}

```dart
AtKey myPublicID = AtKey.public('phone', namespace: 'wavi', sharedBy: '@alice').build();
```

{% endcode %}

#### Self atKey

To create a self atkeyD, first use the `AtKey.self` builder to configure it, then call `.build` to create it.

{% code title="AtKey.self signature" %}

```dart
static SelfKeyBuilder self(String key,
    {String? namespace, String sharedBy = ''})
```

{% endcode %}

The `build` method on `SelfKeyBuilder` takes no parameters.

**Example**

`phone.wavi@alice`

{% code overflow="wrap" %}

```dart
AtKey mySelfID = AtKey.self('phone', namespace: 'wavi', sharedBy: '@alice').build();
```

{% endcode %}

#### Shared atKey

To create a shared atKey, first use the `AtKey.shared` builder to configure it, then call `.build` to create it.

{% code title="AtKey.shared signature" %}

```dart
static SharedKeyBuilder shared(String key,
    {String? namespace, String sharedBy = ''})
```

{% endcode %}

The `build` method on `SharedKeyBuilder` takes no parameters.

**Example**

`@bob:phone.wavi@alice`

{% code overflow="wrap" %}

```dart
AtKey sharedKey = (AtKey.shared('phone', 'wavi')
    ..sharedWith('@bob')).build();
```

{% endcode %}

**Caching shared atKeys**

To cache a shared atKey, you can do a [cascade](https://dart.dev/language/operators#cascade-notation) call on `SharedKeyBuilder.cache`.

{% code title="Caching example" %}

```dart
AtKey atKey = (AtKey.shared('phone', namespace: 'wavi', sharedBy: '@alice')
 ..sharedWith('@bob')
 ..cache(1000, true))
 .build();
```

{% endcode %}

### API Docs

You can find the API reference for the entire package available on [pub](https://pub.dev/documentation/at_commons/latest/).

The `AtKey` class API reference is available [here](https://pub.dev/documentation/at_commons/latest/at_commons/AtKey-class.html).
{% endtab %}

{% tab title="C" %}

## C

You can find all these examples also on our [GitHub](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c).

### Introduction

There are three kinds of atKeys:

1. [Public atKey](#public-atkey)
2. [Self atKey](#self-atkey)
3. [Shared atKey](#shared-atkey)

[Public atKey](#public-atkey) holds public (and non-encrypted) data, available for any Atsign to get from you.&#x20;

[Self atKey](#self-atkey) holds self encrypted data, only available for your own Atsign to get.&#x20;

[Shared atKey](#shared-atkey) holds encrypted data that is only decipherable by you and the intended recipient.

Every atKey has [metadata](#metadata), which is free for you to also control (to an extent). Not all metadata should be handled by the developer. Some metadata is managed by the SDK itself. Check out our [documentation](/atsign-platform/atrecord) on metadata to find out which metadata is worth your time handling.

Before running any of the examples, be sure to include `atkey.h`

```c
#include <atclient/atkey.h>
```

### Public atKey

This is how you create a Public atKey.

First, create the atkey struct.

```c
atclient_atkey my_public_atkey;
atclient_atkey_init(&my_public_atkey);
```

Next, call the `atclient_atkey_create_public_key` function.

```c
const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_namespace = "wavi"; 

if(atclient_atkey_create_public_key(
  &my_public_atkey,
  atkey_key,
  atkey_shared_by,
  atkey_namespace) != 0) {
  // an error occurred
}
```

Don't forget to call `atclient_atkey_free` at the end of the intended life-time of your atkey struct.

```c
atclient_atkey_free(&my_public_atkey);
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    /*
     * Create an atkey struct
     */
    atclient_atkey my_public_atkey;
    atclient_atkey_init(&my_public_atkey);

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_namespace = "wavi"; 

    /*
     * Use the dandy atkey_create_public_key function to populate the struct for you with your desired values.
     */
    if((exit_code = atclient_atkey_create_public_key(&my_public_atkey, atkey_key, atkey_shared_by, atkey_namespace)) != 0) {
        goto exit;
    }

    exit_code = 0;
exit:
{
    atclient_atkey_free(&my_public_atkey);
    return exit_code;
}
}
```

### Self atKey

This is how you create a Self atKey.

First, create the `atclient_atkey struct`

<pre class="language-c"><code class="lang-c"><strong>atclient_atkey my_self_atkey;
</strong>atclient_atkey_init(&#x26;my_self_atkey);
</code></pre>

Next, call the `atclient_atkey_create_self_key` function.

```c
const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_namespace = "wavi";

if(atclient_atkey_create_self_key(
  &my_self_atkey,
  atkey_key,
  atkey_shared_by,
  atkey_namespace) != 0) {
  // an error occurred
}
```

Don't forget to call `atclient_atkey_free` at the end of the intended life-time of your atkey struct.

```c
atclient_atkey_free(&my_public_atkey);
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    /*
     * Create an atkey struct
     */
    atclient_atkey my_self_atkey;
    atclient_atkey_init(&my_self_atkey);

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_namespace = "wavi";

    /*
     * Use the dandy atkey_create_self_key function to populate the struct for you with your desired values.
     */
    if((exit_code = atclient_atkey_create_self_key(&my_self_atkey, atkey_key, atkey_shared_by, atkey_namespace)) != 0) {
        goto exit;
    }

    exit_code = 0;
exit:
{
    atclient_atkey_free(&my_self_atkey);
    return exit_code;
}
}
```

### Shared atKey

This is how you create a Shared atKey.

First, create the `atclient_atkey` struct.

```c
atclient_atkey my_shared_atkey;
atclient_atkey_init(&my_shared_atkey);
```

Next, call the `atclient_atkey_create_shared_key` function.

```c
const char *atkey_key = "phone";
const char *atkey_shared_by = ATSIGN;
const char *atkey_shared_with = "@soccer99";
const char *atkey_namespace = "wavi";

if(atclient_atkey_create_shared_key(&my_shared_atkey,
  atkey_key,
  atkey_shared_by,
  atkey_shared_with, 
  atkey_namespace) != 0) {
  // an error occurred
}
```

Don't forget to call `atclient_atkey_free` at the end of the intended life-time of your atkey struct.

```c
atclient_atkey_free(&my_public_atkey);
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    /*
     * Create an atkey struct
     */
    atclient_atkey my_shared_atkey;
    atclient_atkey_init(&my_shared_atkey);

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_shared_with = "@soccer99";
    const char *atkey_namespace = "wavi";

    /*
     * Use the dandy atkey_create_shared_key function to populate the struct for you with your desired values.
     */
    if((exit_code = atclient_atkey_create_shared_key(&my_shared_atkey, atkey_key, atkey_shared_by, atkey_shared_with, atkey_namespace)) != 0) {
        goto exit;
    }


    exit_code = 0;
exit:
{
    atclient_atkey_free(&my_shared_atkey);
    return exit_code;
}
}
```

### Metadata

This is how you modify the metadata of any atKey. For the sake of this example, we will modify the metadata of a Shared atKey.

First, create your atKey as usual.

```c
atclient_atkey my_shared_atkey;
atclient_atkey_init(&my_shared_atkey);

const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_shared_with = "@soccer99";
const char *atkey_namespace = "wavi";

if (atclient_atkey_create_shared_key(&my_shared_atkey, atkey_key, atkey_shared_by, atkey_shared_with, atkey_namespace) != 0)
{
  // an error occurred
}
```

Next, let's create a pointer to the atKey's metadata. We can feel safe reading the `atclient_atkey`'s inner metadata field because we have previously called `atclient_atkey_init`.

```c
atclient_atkey_metadata *metadata = &(my_shared_atkey.metadata);
```

Call a `atclient_atkey_metadata_set_*` function. For the sake of this example, we will call `atclient_atkey_metadata_set_ttl` which sets the atKey's lifespan, specified in milliseconds.

```c
if (atclient_atkey_metadata_set_ttl(metadata, 1 * 1000) != 0)
{
    // an error occurred
}
```

We can check if that was set and is safe to read by using the `atclient_atkey_metadata_is_ttl_initialized(metadata)` function. It is important to check that `ttl` was initialized. If this function returns true, we can feel safe knowing that the the internal initialized bit was set to true previously and that we are not reading garbage data.

```c
if(atclient_atkey_metadata_is_ttl_initialized(metadata)) {
    // metadata->ttl is safe to read and we know it is populated.
    printf("metadata->ttl: %d\n", metadata->ttl);
}
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

#define ATSERVER_HOST "root.atsign.org"
#define ATSERVER_PORT 64

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    atclient_atkey my_shared_atkey;
    atclient_atkey_init(&my_shared_atkey);

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_shared_with = "@soccer99";
    const char *atkey_namespace = "wavi";

    if ((exit_code = atclient_atkey_create_shared_key(&my_shared_atkey, atkey_key, atkey_shared_by, atkey_shared_with, atkey_namespace)) != 0)
    {
        goto exit;
    }

    /*
     * Now that the AtKey is properly set up, we can set the metadata of that AtKey like this.
     * Since we called the `atclient_atkey_init` function earlier, we can feel safe knowing that the metadata is also already initialized.
     */
    atclient_atkey_metadata *metadata = &(my_shared_atkey.metadata);

    /*
     * Set the ttl (time to live) of the AtKey. Once this AtKey is put into the atServer, it will only live for 1000 milliseconds.
     */
    if ((exit_code = atclient_atkey_metadata_set_ttl(metadata, 1 * 1000)) != 0)
    {
        goto exit;
    }

    /*
     * Set the ttr (time to refresh) of the AtKey. Once this AtKey is put into the atServer, the recipient of the AtKey will have it refreshed every 1000
     * milliseconds, in case there are any value changes.
     */
    if ((exit_code = atclient_atkey_metadata_set_ttr(metadata, 1 * 1000)) != 0)
    {
        goto exit;
    }

    if(atclient_atkey_metadata_is_ttl_initialized(metadata)) {
        // metadata->ttl is safe to read and we know it is populated.
        atlogger_log("main", ATLOGGER_LOGGING_LEVEL_DEBUG, "metadata->ttl: %d\n", metadata->ttl); // [DEBG] 2024-08-15 01:52:09.596055 | main | metadata->ttl: 1000
    }

    exit_code = 0;
exit:
{
    atclient_atkey_free(&my_shared_atkey); // this _free command will automatically free the metadata as well
    return exit_code;
}
}
```

x
{% endtab %}
{% endtabs %}


# CRUD Operations

How to do basic CRUD operations on an atServer

{% tabs %}
{% tab title="Flutter / Dart" %}
In Dart, the AtClient is stored within the AtClientManager. Once an Atsign has been [onboarded](/atsdk/sdk/onboarding), you will be able to access the AtClientManager for its associated atSign.

### AtClientManager

AtClientManager is a [singleton](https://en.wikipedia.org/wiki/Singleton_pattern) model. When `AtClientManager.getInstance()` is called, it will get the AtClientManager instance for the last onboarded Atsign.

```dart
AtClientManager atClientManager = AtClientManager.getInstance();
```

{% hint style="info" %}
If you need simultaneous access to multiple atClients, you need to create a new [isolate](https://dart.dev/language/concurrency#how-isolates-work) for each additional atClient, and onboard its Atsign within the isolate.

An example of this pattern can be found in [at\_daemon\_server](https://github.com/atsign-foundation/at_services/tree/trunk/packages/at_daemon_server/lib/src/server).
{% endhint %}

### AtClient

As previously mentioned, the AtClientManager stores the actual AtClient itself. You can retrieve the `AtClient` by calling `atClientManager.atClient`.

```dart
AtClient atClient = atClientManager.atClient;
```

#### atKey

Before you can do anything with an atRecord, you need an [atKey](/atsign-platform/atrecord#atidentifier) to represent it.

If you don't know how to create an atKey, please see the [reference](/atsdk/sdk/atid-reference) first.

{% hint style="info" %}
The following examples use the self atKey `phone.wavi@<current atSign>`

It is up to the developer to modify the atKey according to their use case.
{% endhint %}

#### Creating / Updating Data

To create data the `put` method is used, this method accepts text (`String`) or binary (`List<int>`).

{% code overflow="wrap" %}

```dart
String currentAtSign = atClient.getCurrentAtSign()!;
AtKey myID = AtKey.self('phone', namespace: 'wavi',
                        sharedBy: currentAtSign).build();
String dataToStore = "123-456-7890";
bool res = await atClient.put(myID, dataToStore);
```

{% endcode %}

<pre class="language-dart" data-title="put signature"><code class="lang-dart">Future&#x3C;bool> put(
    AtKey key,
    dynamic value,
    {bool <a data-footnote-ref href="#user-content-fn-1">isDedicated</a> = false,
<strong>    PutRequestOptions? putRequestOptions});
</strong></code></pre>

**Strongly Typed Methods**

Since `put` accepts both `String` or `List<int>` as a value, the typing is dynamic. atClient also contains the strongly typed `putText` which only accepts `String` for the value, or `putBinary` which only accepts `List<int>` for the value.

**To update existing data**

Updating existing data is done by doing a put to the same atKey, this will overwrite any existing data stored in the atRecord.

```dart
List<int> binaryData = [1, 2, 3, 4];
bool res = await atClient.put(myID, binaryData);
```

The bytes `[1, 2, 3, 4]` have now replaced the string `"123-456-7890"`.

#### Reading Data

There are two parts to reading data using the atClient SDK:

1. Scanning for and listing out the atKeys for atRecords that can be retrieved
2. Retrieving the atRecord for a given atKey

**1. Scanning and listing** atKey**s**

There are two methods available for scanning and listing atKeys:

1. `getAtKeys` which provides the list in Class format (i.e. `List<AtKey>`)
2. `getKeys` which provides the list in String format (i.e. `List<String>`)

Both methods accept the same parameters, only the return type is different. So we will show the more commonly used getAtKeys signature:

{% code title="getAtKeys signature" %}

```dart
Future<List<AtKey>> getAtKeys(
    {String? regex,
    String? sharedBy,
    String? sharedWith,
    bool showHiddenKeys = false});
```

{% endcode %}

*regex*

A regular expression used to filter the list of atKeys.

*sharedBy*

Filter the list of atKeys to only include ones shared by a particular Atsign.

*sharedWith*

Filter the list of atKeys to only include ones shared with a particular Atsign.

*showHiddenKeys*

A boolean flag to enable the inclusion of hidden atKeys (default = `false`)

**Usage**

Get all available (non-hidden) atKeys:

```dart
List<AtKey> allIDs = await atClient.getAtKeys();
```

All atKeys which end with ".wavi" in the record identifier part:

```dart
List<AtKey> waviIDs = await atClient.getAtKeys(regex: '^.*\.wavi@.+$');
```

**2. Retrieving atRecords by** atKey

To retrieve an atRecord, you must know the atKey and pass it to the `get` method.

{% code title="get signature" %}

```dart
Future<AtValue> get(
    AtKey key,
    {bool isDedicated = false,
    GetRequestOptions? getRequestOptions});
```

{% endcode %}

Calling this function will return an AtValue, and update the atKey passed to it with any changes to the metadata.

**Usage**

<pre class="language-dart"><code class="lang-dart">AtValue atValue = await atClient.get(myID);
String? text = atValue.<a data-footnote-ref href="#user-content-fn-2">value</a>;
</code></pre>

#### Deleting Data

To delete data, simply call the `delete` method with the atKey for the atRecord to delete.

{% code title="delete signature" %}

```dart
Future<bool> delete(
    AtKey key,
    {bool isDedicated = false});
```

{% endcode %}

**Usage**

```dart
bool res = await atClient.delete(myID);
```

### Additional Features

See [#additional-features](#additional-features "mention") to learn about synchronization, which supports syncing of a local atServer, allowing CRUD operations to work even if the application has no internet access.

### API Docs

You can find the API reference for the entire package available on [pub](https://pub.dev/documentation/at_client/latest/).

The `AtClient` class API reference is available [here](https://pub.dev/documentation/at_client/latest/at_client/AtClient-class.html).
{% endtab %}

{% tab title="C" %}

## C

You can find all of these examples on our [GitHub](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c).

### Table of Contents

* [#introduction](#introduction "mention")
* [#put-public-atkey](#put-public-atkey "mention")
  * [#id-1.-create-public-atkey](#id-1.-create-public-atkey "mention")
  * [#id-2.-call-atclient\_put\_public\_key](#id-2.-call-atclient_put_public_key "mention")
  * [#example-application](#example-application "mention")
* [#put-self-atkey](#put-self-atkey "mention")
  * [#id-1.-create-a-self-atkey](#id-1.-create-a-self-atkey "mention")
  * [#id-2.-call-atclient\_put\_self\_key](#id-2.-call-atclient_put_self_key "mention")
  * [#example-application-1](#example-application-1 "mention")
* [#put-shared-atkey](#put-shared-atkey "mention")
  * [#id-1.-create-a-shared-atkey](#id-1.-create-a-shared-atkey "mention")
  * [#id-2.-call-atclient\_put\_shared\_key](#id-2.-call-atclient_put_shared_key "mention")
  * [#example-application-2](#example-application-2 "mention")
* [#get-public-atkey](#get-public-atkey "mention")
  * [#id-1.-create-public-atkey-1](#id-1.-create-public-atkey-1 "mention")
  * [#id-2.-call-atclient\_get\_public\_key](#id-2.-call-atclient_get_public_key "mention")
  * [#id-3.-free-value](#id-3.-free-value "mention")
  * [#example-application-3](#example-application-3 "mention")
* [#get-self-atkey](#get-self-atkey "mention")
  * [#id-1.-create-a-self-atkey-1](#id-1.-create-a-self-atkey-1 "mention")
  * [#id-2.-call-atclient\_get\_self\_key](#id-2.-call-atclient_get_self_key "mention")
  * [#id-3.-free-value-1](#id-3.-free-value-1 "mention")
  * [#example-application-4](#example-application-4 "mention")
* [#get-shared-atkey](#get-shared-atkey "mention")
  * [#id-1.-create-a-shared-atkey-1](#id-1.-create-a-shared-atkey-1 "mention")
  * [#id-2.-call-atclient\_get\_shared\_key](#id-2.-call-atclient_get_shared_key "mention")
  * [#id-3.-free-value-2](#id-3.-free-value-2 "mention")
  * [#example-application-5](#example-application-5 "mention")
* [#delete-an-atkey](#delete-an-atkey "mention")
  * [#id-1.-create-an-atkey](#id-1.-create-an-atkey "mention")
  * [#id-2.-call-atclient\_delete](#id-2.-call-atclient_delete "mention")
  * [#example-application-6](#example-application-6 "mention")
* [#request-options](#request-options "mention")

### Introduction

In this section, we will learn how to `put`, `get`, and `delete` an atKey.

If you are unfamiliar with the different atKey types, check out our documentation on [atRecords](/atsign-platform/atrecord).

### Put Public atKey

#### 1. Create Public atKey

First, create a public atKey. It is important to note that the `shared_by` Atsign should be the same as the authenticated Atsign in the application.

```c
atclient_atkey my_public_atkey;
atclient_atkey_init(&my_public_atkey);

const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_namespace = "c_demos";

if (atclient_atkey_create_public_key(&my_public_atkey, atkey_key, atkey_shared_by, atkey_namespace) != 0)
{
  // an error occurred
}
```

#### 2. Call \`atclient\_put\_public\_key\`

Next, simply \*put\* the value into your atServer. Since we are putting a public value into our atServer, no data will be encrypted and this data will be available for any Atsign to get.

We will pass `NULL` into the request\_options and commit\_id parameters because we want to use the default options for now and we don't particularly care about the commit\_id that it returns, but you could receive it if you would like.

This function returns an `int` for error handling, in which a non-zero exit code indicates an error.

```c
const char *atkey_value = "123-456-7890";

if (atclient_put_public_key(&atclient, &my_public_atkey, atkey_value, NULL, NULL) != 0)
{
    // an error occurred
}
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient;
    atclient_init(&atclient);

    atclient_atkey my_public_atkey;
    atclient_atkey_init(&my_public_atkey);

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_namespace = "c_demos";

    if ((exit_code = atclient_atkey_create_public_key(&my_public_atkey, atkey_key, atkey_shared_by, atkey_namespace)) != 0)
    {
        goto exit;
    }

    const char *atkey_value = "123-456-7890";

    /*
     * atclient_put_self_key lets you put a key-value pair in your atSign's atServer.
     * For our purposes, we will pass `NULL` for the request options and the commit id. 
     * We want to use the default options and we don't want to receive and
     * store the commit id.
     */
    if ((exit_code = atclient_put_public_key(&atclient, &my_public_atkey, atkey_value, NULL, NULL)) != 0)
    {
        goto exit;
    }

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient);
    atclient_atkey_free(&my_public_atkey);
    return exit_code;
}
}
```

### Put Self atKey

#### 1. Create a Self atKey

```c
atclient_atkey my_self_atkey;
atclient_atkey_init(&my_self_atkey);

const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_namespace = "c_demos";

if (atclient_atkey_create_self_key(&my_self_atkey, atkey_key, atkey_shared_by, atkey_namespace) != 0)
{
    // an error occurred
}
```

#### 2. Call \`atclient\_put\_self\_key\`

This will put a value specially encrypted for your atServer that only the Atsign's atKeys can decrypt.

We will pass `NULL` into the request\_options and commit\_id parameters because we want to use the default options for now and we don't particularly care about the commit\_id that it returns, but you could receive it if you would like.

This function will return an `int` for error handling, in which a non-zero exit code indicates an error.&#x20;

```c
const char *atkey_value = "123-456-7890";

if (atclient_put_self_key(&atclient, &my_self_atkey, atkey_value, NULL, NULL) != 0)
{
  // an error occurred
}
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient;
    atclient_init(&atclient);

    atclient_atkey my_self_atkey;
    atclient_atkey_init(&my_self_atkey);

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_namespace = "c_demos";

    if ((exit_code = atclient_atkey_create_self_key(&my_self_atkey, atkey_key, atkey_shared_by, atkey_namespace)) != 0)
    {
        goto exit;
    }

    const char *atkey_value = "123-456-7890";

    /*
     * atclient_put_self_key lets you put a key-value pair in your Atsign's atServer.
     * For our purposes, we will pass `NULL` for the request options and the commit id. 
     * We want to use the default options and we don't want to receive and
     * store the commit id.
     */
    if ((exit_code = atclient_put_self_key(&atclient, &my_self_atkey, atkey_value, NULL, NULL)) != 0)
    {
        goto exit;
    }

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient);
    atclient_atkey_free(&my_self_atkey);
    return exit_code;
}
}

```

### Put Shared atKey

#### 1. Create a Shared atKey

```c
atclient_atkey my_shared_atkey;
atclient_atkey_init(&my_shared_atkey);

const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_shared_with = "@soccer0";
const char *atkey_namespace = "c_demos";
if (atclient_atkey_create_shared_key(&my_shared_atkey, atkey_key, atkey_shared_by, atkey_shared_with, atkey_namespace) != 0)
{
    // an error occurred
}
```

#### 2. Call \`atclient\_put\_shared\_key\`

This function will put our string value into the atServer. Since we are using a Shared atKey, that means only the `shared_by` and `shared_with` Atsign will be able to decrypt this value.&#x20;

We will pass `NULL` into the request\_options and commit\_id parameters because we want to use the default options for now and we don't particularly care about the commit\_id that it returns, but you could receive it if you would like.

This function returns an `int` for error handling, in which a non-zero exit code indicates an error.

```c
const char *atkey_value = "123-456-7890";
if (atclient_put_shared_key(&atclient, &my_shared_atkey, atkey_value, NULL, NULL) != 0)
{
    // an error occurred
}
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient;
    atclient_init(&atclient);

    atclient_atkey my_shared_atkey;
    atclient_atkey_init(&my_shared_atkey);

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_shared_with = "@soccer0";
    const char *atkey_namespace = "c_demos";

    if ((exit_code = atclient_atkey_create_shared_key(&my_shared_atkey, atkey_key, atkey_shared_by, atkey_shared_with, atkey_namespace)) != 0)
    {
        goto exit;
    }

    const char *atkey_value = "123-456-7890";

    /*
     * atclient_put_self_key lets you put a key-value pair in your Atsign's atServer.
     * For our purposes, we will pass `NULL` for the request options and the commit id. 
     * We want to use the default options and we don't want to receive and
     * store the commit id.
     */
    if ((exit_code = atclient_put_shared_key(&atclient, &my_shared_atkey, atkey_value, NULL, NULL)) != 0)
    {
        goto exit;
    }

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient);
    atclient_atkey_free(&my_shared_atkey);
    return exit_code;
}
}
```

### Get Public atKey

#### 1. Create Public atKey

First, create a public atKey. It is important to note that the `shared_by` Atsign should be the same as the authenticated Atsign in the application.

```c
atclient_atkey my_public_atkey;
atclient_atkey_init(&my_public_atkey);

const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_namespace = "c_demos";

if (atclient_atkey_create_public_key(&my_public_atkey, atkey_key, atkey_shared_by, atkey_namespace) != 0)
{
  // an error occurred
}
```

#### 2. Call \`atclient\_get\_public\_key\`

We will create a variable named `value` and pass the address to it in our `atclient_get_public_key` function call. The function will allocate memory for us and populate that variable for us.&#x20;

We will pass `NULL` into the request\_options parameter because we want to use the default options for now.

This function returns an `int` for error handling, in which a non-zero exit code indicates an error.

```c
char *value = NULL;
if (atclient_get_public_key(&atclient, &my_public_atkey, &value, NULL) != 0)
{
    // an error occurred
}
```

#### 3. Free \`value\`

Once we are done with the value itself, we should free it to avoid any memory leaks.

```c
free(value);
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient;
    atclient_init(&atclient);

    atclient_atkey my_public_atkey;
    atclient_atkey_init(&my_public_atkey);

    /*
     * Let's declare a null pointer which will later be allocated by our `atclient_get_public_key` function. It is our responsibility to free it once we are
     * done with it.
     */
    char *value = NULL;

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_namespace = "c_demos";

    if ((exit_code = atclient_atkey_create_public_key(&my_public_atkey, atkey_key, atkey_shared_by, atkey_namespace)) != 0)
    {
        goto exit;
    }

    /*
     * `atclient_get_public_key` will allocate memory for the `value` pointer. It is our responsibility to free it once we are done with it.
     * We will pass `NULL` into the request_options argument for now and just use the default request options.
     */
    if ((exit_code = atclient_get_public_key(&atclient, &my_public_atkey, &value, NULL)) != 0)
    {
        goto exit;
    }

    atlogger_log("3d-get-public-atkey", ATLOGGER_LOGGING_LEVEL_INFO, "value: \"%s\"\n", value);

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient);
    atclient_atkey_free(&my_public_atkey);
    free(value);
    return exit_code;
}
}
```

### Get Self atKey

#### 1. Create a Self atKey

```c
atclient_atkey my_self_atkey;
atclient_atkey_init(&my_self_atkey);

const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_namespace = "c_demos";

if (atclient_atkey_create_self_key(&my_self_atkey, atkey_key, atkey_shared_by, atkey_namespace) != 0)
{
    // an error occurred
}
```

#### 2. Call \`atclient\_get\_self\_key\`

We will create a variable named `value` and pass the address to it in our `atclient_get_self_key` function call. The function will allocate memory for us and populate that variable for us.&#x20;

We will pass `NULL` into the request\_options parameter because we want to use the default options for now.

This function returns an `int` for error handling, in which a non-zero exit code indicates an error.

```c
char *value = NULL;
if (atclient_get_self_key(&atclient, &my_self_atkey, &value, NULL) != 0)
{
    // an error occurred
}
```

#### 3. Free \`value\`

Once we are done with the value itself, we should free it to avoid any memory leaks.

```c
free(value);
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient;
    atclient_init(&atclient);

    atclient_atkey my_self_atkey;
    atclient_atkey_init(&my_self_atkey);

    /*
     * Let's declare a null pointer which will later be allocated by our `atclient_get_public_key` function. It is our responsibility to free it once we are
     * done with it.
     */
    char *value = NULL;

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_namespace = "c_demos";

    if ((exit_code = atclient_atkey_create_self_key(&my_self_atkey, atkey_key, atkey_shared_by, atkey_namespace)) != 0)
    {
        goto exit;
    }

    /*
     * `atclient_get_self_key` will allocate memory for the `value` pointer. It is our responsibility to free it once we are done with it.
     * We will pass `NULL` into the request_options argument for now and just use the default request options.
     */
    if ((exit_code = atclient_get_self_key(&atclient, &my_self_atkey, &value, NULL)) != 0)
    {
        goto exit;
    }

    atlogger_log("3E-get-self-atkey", ATLOGGER_LOGGING_LEVEL_INFO, "value: \"%s\"\n", value);

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient);
    atclient_atkey_free(&my_self_atkey);
    free(value);
    return exit_code;
}
}
```

### Get Shared atKey

#### 1. Create a Shared atKey

```c
atclient_atkey my_shared_atkey;
atclient_atkey_init(&my_shared_atkey);

const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_shared_with = "@soccer0";
const char *atkey_namespace = "c_demos";
if (atclient_atkey_create_shared_key(&my_shared_atkey, atkey_key, atkey_shared_by, atkey_shared_with, atkey_namespace) != 0)
{
    // an error occurred
}
```

#### 2. Call \`atclient\_get\_shared\_key\`

We will create a variable named `value` and pass the address to it in our `atclient_get_shared_key` function call. The function will allocate memory for us and populate that variable for us.&#x20;

We will pass `NULL` into the request\_options parameter because we want to use the default options for now.

This function returns an `int` for error handling, in which a non-zero exit code indicates an error.

```c
char *value = NULL;
if (atclient_get_shared_key(&atclient, &my_self_atkey, &value, NULL) != 0)
{
    // an error occurred
}
```

#### 3. Free \`value\`

Once we are done with the value itself, we should free it to avoid any memory leaks.

```c
free(value)
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient;
    atclient_init(&atclient);

    atclient_atkey my_shared_atkey;
    atclient_atkey_init(&my_shared_atkey);

    /*
     * Let's declare a null pointer which will later be allocated by our `atclient_get_public_key` function. It is our responsibility to free it once we are
     * done with it.
     */
    char *value = NULL;

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_shared_with = "@soccer0";
    const char *atkey_namespace = "c_demos";

    if ((exit_code = atclient_atkey_create_shared_key(&my_shared_atkey, atkey_key, atkey_shared_by, atkey_shared_with, atkey_namespace)) != 0)
    {
        goto exit;
    }

    /*
     * `atclient_get_self_key` will allocate memory for the `value` pointer. It is our responsibility to free it once we are done with it.
     * We will pass `NULL` into the request_options argument for now and just use the default request options.
     */
    if ((exit_code = atclient_get_shared_key(&atclient, &my_shared_atkey, &value, NULL)) != 0)
    {
        goto exit;
    }

    atlogger_log("3E-get-self-atkey", ATLOGGER_LOGGING_LEVEL_INFO, "value: \"%s\"\n", value);

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient);
    atclient_atkey_free(&my_shared_atkey);
    free(value);
    return exit_code;
}
}
```

### Delete an atKey

#### 1. Create an atKey

First step is to create the atKey that you wish to delete. This can be of any atKey type (public, self, or shared). What is important to note is that you can only delete an atKey that you own (which means that the authenticated Atsign is the same as the shared\_by Atsign). This should be obvious because you can only delete atKeys that have once been created by you. Only the rightful owners of the atKey that was created can delete it.

For the sake of this demo, we will create a Shared atKey.

```c
atclient_atkey my_shared_atkey;
atclient_atkey_init(&my_shared_atkey);

const char *atkey_key = "phone";
const char *atkey_shared_by = "@jeremy_0";
const char *atkey_shared_with = "@soccer0";
const char *atkey_namespace = "c_demos";
if (atclient_atkey_create_shared_key(&my_shared_atkey, atkey_key, atkey_shared_by, atkey_shared_with, atkey_namespace) != 0)
{
    // an error occurred
}
```

#### 2. Call \`atclient\_delete\`

`atclient_delete` will delete the atKey from your atServer.&#x20;

We will pass `NULL` to the `request_options` and `commit_id` parameter because we want to use the default options for now and we do not care about the commit\_id. You can receive the commit\_id if you would like by passing an int pointer.

It is pointless to set any metadata to the atKey when deleting. Metadata is only useful when getting (you can read any metadata that the key possesses) and putting (you can modify the atKey's behavior). When you are deleting, no metadata is used.

This functions returns an `int` for error handling. A non-zero exit code indicates an error.

```c
if (atclient_delete(&atclient, &my_shared_atkey, NULL, NULL) != 0) {
    // an error occurred
}
```

#### Example Application

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@jeremy_0"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient;
    atclient_init(&atclient);

    atclient_atkey my_shared_atkey;
    atclient_atkey_init(&my_shared_atkey);

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    const char *atkey_key = "phone";
    const char *atkey_shared_by = ATSIGN;
    const char *atkey_shared_with = "@soccer0";
    const char *atkey_namespace = "c_demos";

    if ((exit_code = atclient_atkey_create_shared_key(&my_shared_atkey, atkey_key, atkey_shared_by, atkey_shared_with, atkey_namespace)) != 0)
    {
        goto exit;
    }

    /*
     * `atclient_delete` will delete this shared atkey from our atServer. It is important to note that only the `shared_by` Atsign can delete the shared atKey
     * When deleting, the `shared_by` Atsign should always be the authenticated Atsign in the `atclient` object.
     * We will pass `NULL` to the request_options and commit_id parameters because we want to use the default request_options and we don't care about the
     * commit_id we get back.
     */
    if ((exit_code = atclient_delete(&atclient, &my_shared_atkey, NULL, NULL) != 0)) {
        goto exit;
    }

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient);
    atclient_atkey_free(&my_shared_atkey);
    return exit_code;
}
}
```

### Request Options

{% hint style="info" %}
Under Construction
{% endhint %}
{% endtab %}
{% endtabs %}

[^1]: This has been deprecated, and will be ignored.

[^2]: If metaData.isBinary is true, then this will be a List\<int>.


# Notifications

How to send and receive real-time messages

{% tabs %}
{% tab title="Flutter / Dart" %}
In Dart, the AtClient is stored within the AtClientManager. Once an Atign has been [onboarded](/atsdk/sdk/onboarding), you will be able to access the AtClientManager for its associated Atsign.

### AtClientManager

AtClientManager is a [singleton](https://en.wikipedia.org/wiki/Singleton_pattern) model. When `AtClientManager.getInstance()` is called, it will get the AtClientManager instance for the last onboarded atSign.

```dart
AtClientManager atClientManager = AtClientManager.getInstance();
```

{% hint style="info" %}
If you need simultaneous access to multiple atClients, you need to create a new [isolate](https://dart.dev/language/concurrency#how-isolates-work) for each additional atClient, and onboard its Atsign within the isolate.

An example of this pattern can be found in [at\_daemon\_server](https://github.com/atsign-foundation/at_services/tree/trunk/packages/at_daemon_server/lib/src/server).
{% endhint %}

### AtClient

As previously mentioned, the AtClientManager stores the actual AtClient itself. You can retrieve the `AtClient` by calling `atClientManager.atClient`.

```dart
AtClient atClient = atClientManager.atClient;
```

### Monitor

You can subscribe to a stream of notifications like this:

```dart
AtClient atClient = AtClientManager.getInstance().atClient;
atClient.notificationService
  .subscribe(regex: 'message.$nameSpace@', shouldDecrypt: true)
  .listen((notification) {
      print("Got a message: ${notification.value}");
  });
```

### Notify

You can send (a.k.a. publish) a notification like this:

```dart
AtKey messageKey = (AtKey.shared('message', nameSpace)
    ..sharedWith('@bob')).build();
String message = "Hi bob, how are you?";
await atClient.notificationService.notify(
  NotificationParams.forUpdate(messageKey, value: message),
);
```

{% endtab %}

{% tab title="C" %}

## C

You can find the full code of this example on our [GitHub](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c/4a-monitor#4a---monitor).

### Table of Contents

* [#introduction](#introduction "mention")
* [#monitor](#monitor "mention")
  * [#id-1.-include-monitor.h](#id-1.-include-monitor.h "mention")
  * [#id-2.-create-a-monitor-context](#id-2.-create-a-monitor-context "mention")
  * [#id-3.-call-atclient\_monitor\_pkam\_authenticate](#id-3.-call-atclient_monitor_pkam_authenticate "mention")
  * [#id-4.-call-atclient\_monitor\_start](#id-4.-call-atclient_monitor_start "mention")
  * [#id-5.-call-atclient\_monitor\_read](#id-5.-call-atclient_monitor_read "mention")
  * [#id-6.-free-everything](#id-6.-free-everything "mention")
  * [#example-application](#example-application "mention")
* [#notify](#notify "mention")
  * [#id-1.-include-notify.h](#id-1.-include-notify.h "mention")
  * [#id-2.-create-a-shared-atkey](#id-2.-create-a-shared-atkey "mention")
  * [#id-3.-set-up-notify-params](#id-3.-set-up-notify-params "mention")
  * [#id-4.-call-atclient\_notify](#id-4.-call-atclient_notify "mention")
  * [#id-5.-free-everything](#id-5.-free-everything "mention")
  * [#example-application-1](#example-application-1 "mention")

### Introduction

Events is how we send and receive real-time messages in the Atsign Protocol. The client SDK assists in using the Atsign Protocol simply and handles all the complex encryption stuff for you.

* `notify` is how we send a real-time message to an Atsign
* `monitor` is how we listen for real-time messages

### Monitor

#### 1. Include \`monitor.h\`

```c
#include <atclient/monitor.h>
```

#### 2. Create a Monitor Context

First, we need to create a separate monitor connection to our atServer. This is similar to creating a atclient context for conducting CRUD operations, but it is important to make a completely separate one so that connection can be clear of any noise and used purely for monitoring for notifications.&#x20;

```css
atclient monitor_client;
atclient_init(&monitor_client);
```

#### 3. Call \`atclient\_monitor\_pkam\_authenticate\`

This is similar to the `atclient_pkam_authenticate` that you know and love. It should use the same `atserver_host`, `atserver_port`, and `atclient_atkeys` in your original atclient context because we are connecting to the same atServer that can be authenticated using the same set of cryptographic atKeys.

This function will return an `int` for error handling in which a non-zero exit code indicates an error.

```
if (atclient_monitor_pkam_authenticate(
  &monitor_client, 
  atserver_host,
  atserver_port,
  &atkeys,
  "@jeremy_0") != 0) {
    // an error occurred
}
```

#### 4. Call \`atclient\_monitor\_start\`

This will begin the monitoring process of the atclient context that you pass. Since we made a completely different connection, this context will be purely for listening for any notifications.

In this example, we pass `.*` for the regex to receive all incoming notifications.

This function will return an `int` for error handling in which a non-zero exit code indicates an error.

```c
if(atclient_monitor_start(&monitor_client, ".*") != 0) {
    // an error occurred
}
```

#### 5. Call \`atclient\_monitor\_read\`

Calling `atclient_monitor_read` will attempt to read the network buffer for any notifications.&#x20;

It may be useful to put this in a loop to constantly be checking for any notifications that may have arrived to your atServer. You will commonly get error code `-26624` because most times, there will be nothing to read!&#x20;

```c
atclient_monitor_response response;
atclient_monitor_response_init(&response);

int exit_code = atclient_monitor_read(&monitor_client, &atclient1, &response, NULL);

// do things with the `response` variable
if (response.type == ATCLIENT_MONITOR_MESSAGE_TYPE_NOTIFICATION) {
  // we should check if this value is populated first, so that we know it is safe for reading.
  if (atclient_atnotification_is_decrypted_value_initialized(&response.notification)) {
    atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_INFO, "Received notification: %s\n", response.notification.decrypted_value);
  }
}

atclient_monitor_response_free(&response);
```

See the [#example-application-1](#example-application-1 "mention") on how to read the different kinds of values from the `atclient_monitor_response` struct.

#### 6. Free everything

Don't forget to free everything at the end of your application's life time

```c
atclient_free(&monitor_client);
```

Also free the `atclient_monitor_response` struct if you haven't already.

```c
atclient_monitor_response_free(&response);
```

#### Example Application

You can find the full code of this example on our [GitHub](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c/4a-monitor#4a---monitor).

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atclient/monitor.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define TAG "4a-monitor"

#define ATSIGN "@soccer99"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient1;
    atclient_init(&atclient1);

    atclient monitor_client;
    atclient_init(&monitor_client);

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient1, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    /*
     * Our monitor connection is just an ordinary atclient context, but we will use a completely separate atclient context to manage it.
     * And instead of calling the atclient functions on our atclient monitor context, we will use monitor functions on it instead.
     * In this case, we are using `atclient_monitor_pkam_authenticate` instead of `atclient_pkam_authenticate` even though it's essentially the same function
     * signature (aside from the function name)
     */
    if ((exit_code = atclient_monitor_pkam_authenticate(&monitor_client, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if((exit_code = atclient_monitor_start(&monitor_client, ".*")) != 0) {
        goto exit;
    }

    while (true)
    {
        atclient_monitor_response response;
        atclient_monitor_response_init(&response);

        exit_code = atclient_monitor_read(&monitor_client, &atclient1, &response, NULL);

        if (exit_code != 0)
        {
            if (response.type == ATCLIENT_MONITOR_ERROR_READ)
            {
                atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Error reading from monitor: %d\n", response.error_read.error_code);
            }
            else if (response.type == ATCLIENT_MONITOR_ERROR_PARSE_NOTIFICATION)
            {
                atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Parse notification from monitor: %d\n", exit_code);
            }
            else if (response.type == ATCLIENT_MONITOR_ERROR_DECRYPT_NOTIFICATION)
            {
                atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Error response from monitor: %d\n", exit_code);
            } else {
                atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Unknown error: %d\n", exit_code);
            }
        }
        else
        {

            if (response.type == ATCLIENT_MONITOR_MESSAGE_TYPE_NOTIFICATION)
            {
                // we should check if this value is populated first, so that we know it is safe for reading.
                if (atclient_atnotification_is_decrypted_value_initialized(&response.notification))
                {
                    atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_INFO, "Received notification: %s\n", response.notification.decrypted_value);
                }
            }
            else if (response.type == ATCLIENT_MONITOR_MESSAGE_TYPE_DATA_RESPONSE)
            {
                atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_INFO, "Received data: %s\n", response.data_response); // most times, this is a `data:ok` from a heart beat
            }
            else if (response.type == ATCLIENT_MONITOR_MESSAGE_TYPE_ERROR_RESPONSE)
            {
                atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Received error: %s\n", response.error_response);
            }
            else if (response.type == ATCLIENT_MONITOR_MESSAGE_TYPE_NONE)
            {
                atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_INFO, "No message received\n");
            } else {
                atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_ERROR, "Unknown message type: %d\n", response.type);
            }
        }

        atclient_monitor_response_free(&response);
    }

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient1);
    atclient_free(&monitor_client);
    return exit_code;
}
}

```

### Notify

#### 1. Include \`notify.h\`

```c
#include <atclient/notify.h>
```

#### 2. Create a Shared atKey

Since we are notifying another Atsign, we have to set up a Shared atKey.&#x20;

This process should be familiar to you if you have already gone through [CRUD Operations](/atsdk/sdk/crud-operations).

```c
atclient_atkey atkey;
atclient_atkey_init(&atkey);

if(atclient_atkey_create_shared_key(&atkey, "phone", "@jeremy_0", "@soccer99", "c_demos") != 0) {
    // an error occurred
}
```

#### 3. Set up Notify Params

Next, we will set up the parameters before sending the notification. This will essentially hold the instructions on how to send the notification.

The `atKey` and `value` are the two fields that are mandatory in order to send a notification. If you forget to set these two fields, then you will not be allowed to send a notification because the instructions of sending a notification are incomplete.

```c
atclient_notify_params notify_params;
atclient_notify_params_init(&notify_params);

if(atclient_notify_params_set_atkey(&notify_params, &atkey) != 0) {
  // an error occurred
}

const char *value = "Hello! This is a notification!";
if(atclient_notify_params_set_value(&notify_params, value) != 0) {
  // an error occurred
}
```

#### 4. Call \`atclient\_notify\`

Simply call `atclient_notify` .This will send a notification to the `shared_with` Atsign that you specified when creating the Shared atKey which was done in this [step](#id-2.-create-a-shared-atkey).

We will pass `NULL` into the `commit_id` parameter because we don't reallty care about the commt id for now. You can receive it by passing a `int *` if you would like.

```c
if(atclient_notify(&atclient1, &notify_params, NULL) != 0) {
  // an error occurred
}
```

#### 5. Free everything

Don't forget to free everything

```c
atclient_atkey_free(&atkey);
atclient_notify_params_free(&notify_params);
```

#### Example Application

You can find the full code of this example on our [GitHub](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c/4b-notify#4b---notify).

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/constants.h>
#include <atclient/notify.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define ATSIGN "@soccer0"

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient1;
    atclient_init(&atclient1);

    atclient_atkey atkey;
    atclient_atkey_init(&atkey);

    atclient_notify_params notify_params;
    atclient_notify_params_init(&notify_params);

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient1, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    /*
     * 1a. Set up atkey in our notify_params struct
     */
    if((exit_code = atclient_atkey_create_shared_key(&atkey, "phone", ATSIGN, "@soccer99", "c_demos")) != 0) {
        goto exit;
    }

    if((exit_code = atclient_notify_params_set_atkey(&notify_params, &atkey)) != 0) {
        goto exit;
    }

    /*
     * 1b. Set up value in our notify_params struct
     */
    const char *value = "Hello! This is a notification!";
    if((exit_code = atclient_notify_params_set_value(&notify_params, value)) != 0) {
        goto exit;
    }

    /*
     * 2. Send the notification
     * We will pass `NULL` for the commit id.
     */
    if((exit_code = atclient_notify(&atclient1, &notify_params, NULL)) != 0) {
        goto exit;
    }

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient1);
    atclient_atkey_free(&atkey);
    atclient_notify_params_free(&notify_params);
    return exit_code;
}
}
```

{% endtab %}
{% endtabs %}


# Additional Features

Implementation specific features to know about

Some atSDKs have additional features, usually due to some technical reason. You can find a list of those features and which platforms/implementations support them here.

<table><thead><tr><th width="214">Feature</th><th width="318">What it does</th><th>Platform(s) Supported</th></tr></thead><tbody><tr><td><a data-mention href="/pages/z8byaPIewXlZsGioRFLU">/pages/z8byaPIewXlZsGioRFLU</a></td><td>Offline support for apps</td><td>Flutter / Dart</td></tr><tr><td><a data-mention href="/pages/nNe8YAzRoeNCVIthlNVD">/pages/nNe8YAzRoeNCVIthlNVD</a></td><td>Control the connection lifecycle</td><td>C</td></tr></tbody></table>


# Synchronization

{% tabs %}
{% tab title="Flutter / Dart" %}

## Flutter / Dart

### SyncService

SyncService syncs the client app and remote secondary server’s changes:

* If the client app’s changes are ahead, it pushes the changes to the remote secondary.
* If the remote secondary is ahead, it pulls the changes to the client app.

### Usage

To trigger a sync, call the `.sync` function:

```dart
AtClientManager atClientManager = AtClientManager.getInstance();
SyncService syncService = atClientManager.syncService;
syncService.sync();
```

{% endtab %}
{% endtabs %}


# Connection Hooks

{% tabs %}
{% tab title="C" %}

## C

The full example code can be found on our [GitHub](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c/5a-hooks#5a---hooks).

### Connection Hooks

Connections hooks are useful for when you want to add additional control to the connection lifecycle in the atSDK. This means you as the developer can implement things like automatically reconnecting when connections drop (example below).

### Usage

#### 1. Enable hooks in your context's atserver\_connection

Since not every connection uses hooks, you must explicitly enable it.&#x20;

```c
atclient_connection_hooks_enable(&(atclient->atserver_connection));
```

#### 2. Write a void\* function

Write a function that we would like our atclient connection to run at certain times during the program.

```c
void *pre_write_hook(atclient_connection_hook_params *params)
{
    atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_INFO, "pre_write_hook called\n");
    return NULL;
}
```

#### 3. Set the hook

For example, we can set the `ATCLIENT_CONNECTION_HOOK_TYPE_PRE_WRITE` hook to run our function `pre_write_hook` before we write any data to our atServer connection.

```c
atclient_connection_hooks_set(&(atclient->atserver_connection), ATCLIENT_CONNECTION_HOOK_TYPE_PRE_WRITE, pre_write_hook))
```

### Examples

#### Print something before we write to the atServer

The full example code can be found on our [GitHub](https://github.com/atsign-foundation/at_demos/tree/trunk/demos/get_started_c/5a-hooks#5a---hooks).

```c
#include <atclient/atclient.h>
#include <atclient/atclient_utils.h>
#include <atclient/connection_hooks.h>
#include <atclient/constants.h>
#include <atclient/notify.h>
#include <atlogger/atlogger.h>
#include <stdlib.h>

#define TAG "5a-hooks"

#define ATSIGN "@soccer0"

void *pre_write_hook(atclient_connection_hook_params *params)
{
    atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_INFO, "pre_write_hook called\n");
    return NULL;
}

int setup_hooks(atclient *atclient)
{
    int exit_code;

    if ((exit_code = atclient_connection_hooks_enable(&(atclient->atserver_connection))) != 0)
    {
        return exit_code;
    }

    if ((exit_code = atclient_connection_hooks_set(&(atclient->atserver_connection), ATCLIENT_CONNECTION_HOOK_TYPE_PRE_WRITE, pre_write_hook)) != 0)
    {
        return exit_code;
    }

    exit_code = 0;
    return exit_code;
}

int main()
{
    int exit_code = -1;

    atlogger_set_logging_level(ATLOGGER_LOGGING_LEVEL_DEBUG);

    char *atserver_host = NULL;
    int atserver_port = 0;

    atclient_atkeys atkeys;
    atclient_atkeys_init(&atkeys);

    atclient atclient1;
    atclient_init(&atclient1);

    atclient_atkey atkey;
    atclient_atkey_init(&atkey);

    atclient_notify_params notify_params;
    atclient_notify_params_init(&notify_params);

    if ((exit_code = atclient_utils_find_atserver_address(ATCLIENT_ATDIRECTORY_PRODUCTION_HOST, ATCLIENT_ATDIRECTORY_PRODUCTION_PORT, ATSIGN, &atserver_host, &atserver_port)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_utils_populate_atkeys_from_homedir(&atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_pkam_authenticate(&atclient1, atserver_host, atserver_port, &atkeys, ATSIGN)) != 0)
    {
        goto exit;
    }

    /*
     * Set up hooks so that we can run code before our connection writes to the atServer.
     */
    if ((exit_code = setup_hooks(&atclient1)) != 0)
    {
        goto exit;
    }
    atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_INFO, "Hooks set up successfully.\n");

    /*
     * Now at this point, it's business as normal.
     * For the sake of this example, we're going to send a notification.
     * Since we set up a pre write hook, it will log a message before we send any data to the atServer.
     */
    if ((exit_code = atclient_atkey_create_shared_key(&atkey, "phone", ATSIGN, "@soccer99", "c_demos")) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_notify_params_set_atkey(&notify_params, &atkey)) != 0)
    {
        goto exit;
    }

    const char *value = "Hello! This is a notification!";
    if ((exit_code = atclient_notify_params_set_value(&notify_params, value)) != 0)
    {
        goto exit;
    }

    if ((exit_code = atclient_notify(&atclient1, &notify_params, NULL)) != 0)
    {
        goto exit;
    }

    exit_code = 0;
exit:
{
    free(atserver_host);
    atclient_atkeys_free(&atkeys);
    atclient_free(&atclient1);
    atclient_atkey_free(&atkey);
    atclient_notify_params_free(&notify_params);
    return exit_code;
}
}
```

#### Automatically reconnect when the connection drops

```c
// TODO
```

{% endtab %}
{% endtabs %}


# atSDK Walkthroughs

Explore walkthroughs for building on Atsign Platform across multiple languages. Start with the guide available today, and check back as we expand support for additional languages.

<table data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-type="image">Cover image (dark)</th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Dart atSDK Walkthrough</strong></td><td data-object-fit="contain"><a href="/files/bpQMBcw16JEZN3NQlVxR">/files/bpQMBcw16JEZN3NQlVxR</a></td><td></td><td></td><td><a href="/pages/RFSz3vVxA5PmIzvsRCgc">/pages/RFSz3vVxA5PmIzvsRCgc</a></td></tr><tr><td><strong>C atSDK Walkthrough</strong><br>Coming Soon</td><td><a href="/files/4uBU13Kg0ysSZtEJHkUZ">/files/4uBU13Kg0ysSZtEJHkUZ</a></td><td><a href="/files/V1j44BWM0aET6xb5YsW4">/files/V1j44BWM0aET6xb5YsW4</a></td><td><a href="/files/V1j44BWM0aET6xb5YsW4">/files/V1j44BWM0aET6xb5YsW4</a></td><td></td></tr><tr><td><strong>Java atSDK Walkthrough</strong><br>Coming Soon</td><td><a href="/files/JSBsBLUDajUu9nFyA4uG">/files/JSBsBLUDajUu9nFyA4uG</a></td><td></td><td><a href="/files/a1xfIw26EIyKguUMMRE8">/files/a1xfIw26EIyKguUMMRE8</a></td><td></td></tr></tbody></table>


# Dart atSDK Walkthrough

Get hands-on with the atSDK. Simple examples to kickstart your development.

{% hint style="info" %}
This guide is primarily targeted towards individuals who want to learn how to build backend / cli based Dart applications with the atSDK. Some of this content will also be applicable to Flutter developers (but not all).
{% endhint %}

### Table of Contents

{% content-ref url="/pages/ryBn96x86Djtwc5DYROp" %}
[Using the atSDK with Dart](/atsdk/atsdk-walkthroughs/atsdk-tutorial/using-the-atsdk-with-dart)
{% endcontent-ref %}

{% content-ref url="/pages/JyTxsMzL6MSfnhrBep5o" %}
[Get sample code](/atsdk/atsdk-walkthroughs/atsdk-tutorial/get-sample-code)
{% endcontent-ref %}

{% content-ref url="/pages/B1CiNQk7mEZz9L2OkWQe" %}
[Cutting your Atsign keys](/atsdk/atsdk-walkthroughs/atsdk-tutorial/cutting-your-atsigns-keys)
{% endcontent-ref %}

{% content-ref url="/pages/xgI5K1YbImk7eVZNH6Gd" %}
[Put and Get data asynchronously](/atsdk/atsdk-walkthroughs/atsdk-tutorial/put-and-get-data-asynchronously)
{% endcontent-ref %}

{% content-ref url="/pages/IwPMbO7f3OpkzBlU4WR7" %}
[Send and Receive data synchronously](/atsdk/atsdk-walkthroughs/atsdk-tutorial/send-and-receive-data-synchronously)
{% endcontent-ref %}

{% content-ref url="/pages/e34THLDq4GHocBfZLSXl" %}
[Remote Procedure Calls (RPC)](/atsdk/atsdk-walkthroughs/atsdk-tutorial/rpc)
{% endcontent-ref %}

{% content-ref url="/pages/o6Mld2Kw7oly5Lm381Ht" %}
[atTalk - Encrypted chat client](/atsdk/atsdk-walkthroughs/atsdk-tutorial/attalk)
{% endcontent-ref %}


# Using the atSDK with Dart

Some very simple example of using the atSDK to get a feel for what it offers and create building blocks for your larger projects.

## What and why Dart ?

Dart is an open source project from Google offering a fast and multi-platform programming language. The Atsign team choose Dart as a high-level language to build proof-of-concept code. Dart proved to be a fast and reliable language to build on, and as we needed features like being able to compile to executables, the Dart team delivered.

At this point we have ported the atSDK to other languages, like Java and Python the Dart atSDK is a great place to start.

## Get Dart

Dart is available at [dart.dev](https://dart.dev/get-dart) for Windows, Linux, macOS, download and follow the instructions to install Dart. You can program in the IDE of your choice, but we can recommend [VS Code](https://code.visualstudio.com/) as it has excellent support for Dart, download install VS Code then install the Dart extension.

<figure><img src="/files/toI4W9jHfj9ns8YCP6u3" alt=""><figcaption><p>to get to the Extensions screen press "Control Shift X" then add Dart</p></figcaption></figure>


# Get sample code

## Open Source on GitHub

Atsign Platform and atSDKs are open source. You can find the source code on GitHub and downloaded from [Atsign Foundation](https://github.com/atsign-foundation). This tutorial starts with sample code to demonstrate how to share data and send notifications between Atsigns. These are the basic building blocks for powerful open-source applications like [NoPorts](https://www.noports.com/), which you can use as a reference to see the code in action.

## Clone the demo repo

In VS Code you can click "Clone Git Repository" and enter the URL

```
https://github.com/atsign-foundation/at_demos.git
```

This will copy down all the sample code from GitHub to the directory you select. VS Code will ask you if you want to open the directory you just downloaded and say no. We will pick the one directory we need in the next step.

<figure><img src="/files/jCBrbP9Oh4TjpE6dxc4Z" alt=""><figcaption><p>Clone code from GitHub</p></figcaption></figure>

## Open at\_getting\_started

Click the Open Folder selection and go to the directory you selected and then at\_demos then finally select the at\_getting\_started directory, then click "select folder". You might get a pop up asking if you trust the authors and if you trust us say yes!

At this point you will have the demo code in the IDE and probably see a bunch of red files, meaning something is wrong with them.&#x20;

<figure><img src="/files/5bfkGHH7oKvrUp8bkVPV" alt=""><figcaption><p>dart pub get</p></figcaption></figure>

VS Code if you have the Dart plugin installed will ask if you want to run `pub get` you can safely say yes.

Next you can open a terminal session from the VS Code menu, and you will get a window with a prompt. At that prompt you can type the following two commands to pull down the required libraries.

```
dart pub get
```

```
dart pub update
```

Once you have run those commands in the terminal window all the red text will have gone and we are ready to run some code.

<figure><img src="/files/Q8nS2IZEHm7Kq9EVSzHz" alt=""><figcaption><p>Ready to run.</p></figcaption></figure>


# Cutting your Atsign keys

It takes two to tango!

To complete this tutorial, you  need at least two Atsigns to send and receive data. You can purchase Atsigns at the [Atsign Registrar](http://my.atsign.com/go).&#x20;

Once you have your Atsigns, you are ready to activate them, which means spinning up one atServer per Atsign and cutting your cryptographic keys for each Atsign. Sounds complicated, but it is easy! In fact, it's just a single command. In the terminal window type:

```
dart run .\bin\at_activate.dart 
```

You will get an error message telling you to add the `-a` flag and your atsign, so if your Atsign was @crunchfrog you would type:

```
dart run .\bin\at_activate.dart -a "@crunchyfrog"
```

\*Note on windows the Atsign needs to be in double quotes so the shell does not get confused with the @ symbol. Below you can see the activation process for two Atsigns @energetic22 and @7capricon. Once the command is run you will get and email with the one time password, enter that and the program will create an atKeys file for that Atsign. Any atKeys files you create will be located in your home directory then .atsign/keys.

These keys are important to be kept safe, as they are the only keys to your Atsign and your data. Speaking of data, let's send some between the two Atsigns next.

<figure><img src="/files/qRToQIHObiNKdFQRuwp5" alt=""><figcaption></figcaption></figure>


# Put and Get data asynchronously

You have mail!

## Sending and receiving data

The next step is to send and receive. For this to work, we need to get the atKeys file containing the cryptographic keys and log in or onboard to the atServer. From there we can `put` data and `get` data as if a database was local to us, but, in fact, data is remote on the data owner's atServer.

This is achieved by every data element having an owner (the sending Atsign) and a receiving Atsign, behind the scenes the atSDK encrypts the data and sends it on to the atServer for delivery. All we need to do is a simple:

```dart
await atClient.put(sharedRecordID, message, putRequestOptions: pro);
```

Of course, we also have to set up the atClient object and onboard, but that is all in the `at_key_put.dart` file for you. So let's send some data.&#x20;

&#x20;Running the program without arguments brings up this help, most is self explanatory.

```
dart run .\bin\at_key_put.dart                                                                
    --help                        Usage instructions
-a, --atsign (mandatory)          This client's atSign
-n, --namespace (mandatory)       Namespace
-k, --key-file                    Your atSign's atKeys file if not in ~/.atsign/keys/
-c, --cram-secret                 atSign's cram secret
-h, --home-dir                    home directory
-s, --storage-dir                 directory for this client's local storage files
-d, --root-domain                 Root Domain
                                  (defaults to "root.atsign.org")
-v, --verbose                     More logging
    --never-sync                  Do not run sync
-o, --other-atsign (mandatory)    The atSign we want to communicate with
-m, --message (mandatory)         The message we want to send
Invalid argument(s): Option other-atsign is mandatory.
```

\*Note here we are using the Atsigns activated in the previous section; just substitute your own Atsigns.

```
 dart run .\bin\at_key_put.dart -a "@energetic22" -n "atsign" -o "@7capricorn" -m "hello world" 
 
 dart run .\bin\at_key_get.dart -a "@7capricorn" -n "atsign" -o "@energetic22"
```

The `-n`namespace is lefthand side of the atRecord, so in this case the full atRecord would be `@7capricorn:message.atsign@energetic22` and you see this in the results, too. Experiment for yourself!

<figure><img src="/files/J3oTIUMyrw9VrQsinRkT" alt=""><figcaption></figcaption></figure>

You may also notice that the first time you fail to get the message. This is because we specified in the metadata that the TTL is 60000 millisecond or 1 minute. Try again as you see in the above image and you should get your message too.

```dart
    ..metadata = (Metadata()
      ..ttl = 60000 // expire after 60 seconds
      ..ttr = -1); // allow recipient to keep a cached copy
```

The code is worth taking a look at, as it is less than 70 lines but allows data to be transferred from a device anywhere to a device anywhere, and the data is fully encrypted.&#x20;

The data in this example is "store and forward", which is ideal for many use cases, but sometimes we need near real time data and that's what we cover next.


# Send and Receive data synchronously

Send me a txt!

## Sending and receiving data in near real-time

Sending data, as we saw in the previous example, is pretty easy, but the receiver would have to continually poll unless we had a mechanism to notify the receiving Atsign that they had data to deal with. Fortunately, we have just that with atNotifications.

A notification can be sent to an Atsign and if that Atsign is listening for it, it will create an event that can be handled as soon as the notification is received.

To receive we can use code like this:

```dart
  atClient.notificationService
      .subscribe(regex: 'message.$nameSpace@', shouldDecrypt: true)
      .listen(((notification)
```

And to send code like this:

```dart
  await atClient.notificationService.notify(
      NotificationParams.forUpdate(sharedRecordID, value: message));
```

There are two examples in the repo `at_notify_receive.dart` and `at_notify_send.dart` both contain the basics to send and receive synchronously.&#x20;

To test for yourself, you will need two terminal windows. To do that, you can press the split button on the VS Code IDE.

<figure><img src="/files/xDyGDbn4zpx5vhCg2ybX" alt=""><figcaption></figcaption></figure>

You can then run the following commands again note to replace the Atsigns with your own.

In the right window, run this first to receive:

```dart
dart run .\bin\at_notify_receive.dart -a "@energetic22" -n "atsign"
```

In the left window, you can now send a message using:

```dart
dart run .\bin\at_notify_send.dart -a "@7capricorn" -n "atsign" -o "@energetic22" -m "hello world"
```

The net result should look like this, and your message should appear in the right-hand side.&#x20;

<figure><img src="/files/L9ifZAQGoO23UTGedZoR" alt=""><figcaption><p>Message received.</p></figcaption></figure>

You can try sending different messages and make sure they arrive, you can also put Dart on another machine and talk from machine to machine without setting up any other infrastructure.&#x20;

Neither the sender nor the receiver need any TCP ports open, so this can be used to send end-to-end encrypted messages in near real-time without VPNs/Firewalls being required, just access to the Internet.

If you are thinking this is the basis of a simple end-to-end encrypted chat application, that's coming soon with at\_talk.


# Remote Procedure Calls (RPC)

spooky actions at a distance

## Do something remotely and send back the answer.

RPC has been a design pattern for decades, and, no surprise, we have RPC in the atSDK also.  This demo code is in another directory named `at_rpc_demo`. You can get to it by using VS Code and open folder and select that folder. Next, once again, open two windowpanes.

In the right-hand pane enter (again using your Atsigns!)

```
dart pub get
```

This pulls in needed libraries, and then this starts the RPC listener.

```
dart pub get
dart run .\bin\arithmetic_server.dart -a "@energetic22" -n "atsign" --allow-list "@7capricorn"
```

Then in the left window, you can run up the client

```
dart run .\bin\arithmetic_client.dart -a "@7capricorn" -n "atsign" --server-atsign "@energetic22"
```

You will get a prompt after a second or two and you can put in a math expression and hit enter. The expression will be sent to the other Atsign, the answer calculated, and then returned.&#x20;

See our session in action:

<figure><img src="/files/JAjfnDjBKYOOv2mm9trl" alt=""><figcaption><p>RPC</p></figcaption></figure>

Something to notice here is that the RPC server will only respond to RPCs from the designated Atsign with the `--allow-list` argument.

The RPC can, of course, do anything you want it to, and the Atsigns can be running anywhere.

### The code

An AtRpc object is set up pretty simply with:

```dart
    var rpc = AtRpc(
      atClient: atClient,
      baseNameSpace: atClient.getPreferences()!.namespace!,
      domainNameSpace: 'at_rpc_arithmetic_demo',
      callbacks: DemoRpcServer(),
      allowList: allowList,
    );

    rpc.start();
  } catch (e) {
    print(e);
    print(CLIBase.argsParser.usage);
  }
```

The Callbacks are sent to the `DemoRpcServer`class and handled appropriately.

On the sending side the RPC client is initiated:

```dart
    var rpc = AtRpcClient(
        atClient: atClient,
        baseNameSpace: atClient.getPreferences()!.namespace!,
        domainNameSpace: 'at_rpc_arithmetic_demo',
        serverAtsign: serverAtsign);
```

Then the RPC simple sent and awaiting a reply

```dart
var response = await rpc.call({'expr': expr}).timeout(Duration(seconds: 5));
```


# atTalk - Encrypted chat client

Let's chat!

For the final example we have at\_talk, which is a fully end-to-end encrypted chat application in less than 300 lines of code. There is nothing pretty; it's just a command line app that takes the Unix/Linux command `talk` to a global level.

The command `talk` allows people on a Unix/Linux machine to IM each other, at\_talk allows anyone with an Atsign to talk with each other.

This code is in a separate repo, so, once again, in VS Code click the "Clone Git Repository" button. Then enter:

```
https://github.com/atsign-foundation/at_talk.git
```

<figure><img src="/files/jCBrbP9Oh4TjpE6dxc4Z" alt=""><figcaption></figcaption></figure>

Like before, you will get asked if you want to run `pub get` and should say yes, or, if you prefer, you can open a terminal window and type:

```
 dart pub get
```

This, as we know, pulls in the needed libraries and the code will lose all the red underlines as those libraries are loaded.

At this point, we can open two terminal panels, as we have done before, and run the at\_talk code.  You can run at\_talk without any arguments and it will return some help:

```
dart run .\bin\at_talk.dart
-k, --key-file                Your atSign's atKeys file if not in ~/.atsign/keys/
-a, --atsign (mandatory)      Your atSign
-t, --toatsign (mandatory)    Talk to this atSign
-d, --root-domain             Root Domain (defaults to root.atsign.org)
-n, --namespace               Namespace (defaults to ai6bh)
-v, --verbose                 More logging
    --never-sync              Completely disable sync
Invalid argument(s): Option atsign is mandatory.
```

From here, you can split windows like before and run two at\_talk applications and send messages to each other.

In the left window, for example (subsitute your own Atsigns!

```
 dart run .\bin\at_talk.dart -a "@7capricorn" -t "@energetic22" -n "atsign"
```

In the right window, the same, but in reverse.

```
 dart run .\bin\at_talk.dart -a "@energetic22" -t "@7capricorn" -n "atsign"
```

In this session, you can see the typed messages in white and the received messages in green with the prompts in red.&#x20;

<figure><img src="/files/MAUYN36FkFmJoznoVJI8" alt=""><figcaption><p>talking atSigns</p></figcaption></figure>

Unlike Linux talk, however, these two Atsigns can be anywhere on the Internet and communicating with Privacy. Get a friend to run through the demo and use at\_talk !

There are a couple of features that are worth mentioning: You can use the `/` and then an Atsign to change who you are sending messages to, and, yes, you can message to yourself.

The other feature that has proved very useful is being able to "pipe" commands to at\_talk. To do this, compile the code to a binary using:

```
dart compile exe -o attalk bin/at_talk.dart
```

Now you can "pipe" the output of a command into the chat with the other Atsign.

```
cat .\bin\at_talk.dart| attalk -a "@energetic22" -t "@7capricorn" -n "atsign"
```

This prints the code of at\_talk.dart into the receiving Atsign's chat window. To our knowledge, this is the *only* chat application that you can pipe things to!

If you go this far, first, thank you, and second, please enjoy your continuing journey and raise issues and PRs to any of the repos!


# Atsign AI Architect Overview

An overview of Atsign AI Architect

## TL;DR

Atsign AI Architect is a visual blueprinting tool for designing secure apps and workflows. It interprets nodes, data flows, and agent relationships, then produces a prompt, which when run through an LLM, generates Dart/Flutter projects, Atsign Platform boilerplate, namespaces, key conventions, and multi‑agent patterns automatically.

Atsign AI Architect and the Atsign Platform were recently recognized in an independent Broadband‑Testing Report, earning the Broadband‑Testing Gold Award for delivering secure, zero‑attack‑surface AI applications. [Download the full report](https://exclusive.atsign.com/files/Atsign-Broadband-Testing-Report.pdf?_gl=1*zx2kav*_ga*NDAwMDcxNDkzLjE3NTA3OTYyMjI.*_ga_G3WJETCV6J*czE3ODA1OTMzNjYkbzE5NCRnMSR0MTc4MDU5MzM4MCRqNDYkbDAkaDA.) to read the evaluation and findings.\
\
The tool enforces best practices from Atsign Platform SDKs, including secure data storage, encrypted communication, and backend‑less architecture. Developers can iterate quickly by updating diagrams and regenerating code, ensuring consistency across teams and environments. AI Architect accelerates prototyping, reduces integration errors, and provides a repeatable, standards‑driven workflow for building distributed, privacy‑preserving applications.

{% embed url="<https://vimeo.com/1167932458?fe=ci&fl=sv&share=copy>" %}

For a deeper look at how AI Architect interprets your diagrams, see the [Nodes and Connections](/atsign-ai-architect/atsign-ai-architect-overview/nodes-and-connections) guide, which explains how components and data flows are defined in the canvas and how they map into the generated LLM prompt.


# Nodes and Connections

Not sure when to use what? Here are a few quick guidelines to help you choose:

## Node Palette

These nodes map your workflow visually. Each one represents an actor in your app or agentic system.

<table data-header-hidden data-full-width="false"><thead><tr><th width="154.14453125">Node</th><th width="202.70703125">Description</th><th width="194.58984375">When to use</th><th>Example</th></tr></thead><tbody><tr><td><img src="/files/U0CxkeHH4gMbRkeW0gb7" alt=""></td><td><p><i class="fa-user">:user:</i> <strong>Person</strong><br>People or </p><p>stakeholders</p></td><td>You want to model people's actions, permissions, or decision points.</td><td><mark style="color:$info;">A person logging into an admin dashboard, approving a workflow, or sending a secure message.</mark></td></tr><tr><td><p><img src="/files/I5xfzK2hPxCA3arejQ7F" alt="" data-size="original"></p><p></p></td><td><i class="fa-head-side-circuit">:head-side-circuit:</i> <strong>AI Agent</strong><br>AI Agent</td><td>You want to model automated reasoning, decision-making, or actions performed by an AI system.</td><td><mark style="color:$info;">An AI agent triaging support tickets, summarizing documents, or autonomously executing a multi-step workflow</mark></td></tr><tr><td><img src="/files/NGZvTKcPCtJtnZfl7hwA" alt=""></td><td><p><i class="fa-diamond">:diamond:</i> <strong>Entity</strong></p><p>Businesses, groups of people, etc.</p></td><td>You need access for companies, tenants, departments, identity domains.</td><td><mark style="color:$info;">A healthcare provider, an insurance company, or a tenant account applying its own access rules.</mark></td></tr><tr><td><img src="/files/XkHjnplCi6bjDcp4eWUa" alt=""></td><td><p><i class="fa-cube">:cube:</i> <strong>Thing</strong></p><p>Physical or digital objects/devices</p></td><td>You’re modelling hardware, IoT devices, sensors, servers, or edge components.</td><td><mark style="color:$info;">A smart meter sending readings, a kiosk receiving updates, or a server reporting health status.</mark></td></tr><tr><td><img src="/files/C6onnNDLuwIXtGdZnSjL" alt=""></td><td><i class="fa-gear">:gear:</i> <strong>Process</strong><br>Anything that processes, runs, or executes: an app, service, or workflow</td><td>You want to show business logic, transformations, or background tasks.</td><td><mark style="color:$info;">A data validation step, a policy enforcement engine, or a message-routing workflow.</mark></td></tr><tr><td><img src="/files/Iuz4PdVeFuGqBo2XhpNd" alt=""></td><td><i class="fa-rectangle">:rectangle:</i> <strong>API</strong><br>External systems or platform integrations</td><td>You’re integrating with third-party services or exposing functionality programmatically.</td><td><mark style="color:$info;">A payment processor API, mapping service, or internal microservice endpoint.</mark></td></tr><tr><td><img src="/files/o1IPZr8XpLSsjxBQJdHn" alt=""></td><td><i class="fa-plus">:plus:</i> <strong>Other</strong><br>Custom node type for anything else</td><td>You need to include concepts like storage, policies, models, or abstract systems.</td><td><mark style="color:$info;">A policy store, encryption module, AI model, or logging system.</mark></td></tr></tbody></table>

## Connection Types

These define how the nodes communicate with each other.

<table data-header-hidden><thead><tr><th width="152.171875">Connection</th><th width="271.23828125">Description</th><th width="169.0625">When to use</th><th>Example</th></tr></thead><tbody><tr><td><p><strong>Default</strong></p><p><img src="/files/xPyIavplmqGvZquZmC7C" alt="" data-size="original"></p></td><td><p>Unsure? </p><p>Let the LLM decide.</p></td><td>You're unsure which communication pattern fits, or the interaction doesn't clearly map to a specific type.</td><td><em><mark style="color:$info;">The LLM infers the best pattern based on context and intent</mark></em></td></tr><tr><td><p><strong>Async Comms</strong></p><p><img src="/files/aCr5SkO5MKV2qyKnJAS6" alt="" data-size="original"></p></td><td><p>Asynchronous, non-blocking communication where the sender does not wait for a response and the receiver does not need to be online.</p><p></p><p>Uses <code>Get/Put</code>  semantics.<br><a href="https://docs.atsign.com/tutorials/atsdk-tutorial/put-and-get-data-asynchronously">Read more here</a>.</p></td><td>Really handy if you cannot guarantee both the sender/ receiver will be online at the same time.</td><td><mark style="color:$info;">A device uploads data that is processed later, or a message is delivered when the recipient comes online.</mark></td></tr><tr><td><p><strong>Sync Comms</strong></p><p><img src="/files/I5Qh3BgBFnx7bblXqJw0" alt="" data-size="original"></p></td><td><p>Synchronous communication where the sender waits for an immediate response. <br></p><p>Uses <code>Notification</code>  (<a href="https://docs.atsign.com/tutorials/atsdk-tutorial/send-and-receive-data-synchronously#sending-and-receiving-data-in-near-real-time">Read more here</a>) semantics and/or <code>Get/Put</code>  (<a href="https://docs.atsign.com/tutorials/atsdk-tutorial/put-and-get-data-asynchronously">Read more here</a>) semantics.</p></td><td>You need real-time interaction or confirmation.</td><td><mark style="color:$info;">A user request that must return a result immediately, like fetching account details</mark><sub><mark style="color:$info;">.</mark></sub></td></tr><tr><td><p><strong>Notifications</strong></p><p><img src="/files/sSsevD3a4hCC52PgDGdq" alt=""></p></td><td><p>One-way signals used to alert or inform.</p><p></p><p>Uses <code>Notification</code>  semantics <a href="https://docs.atsign.com/tutorials/atsdk-tutorial/send-and-receive-data-synchronously#sending-and-receiving-data-in-near-real-time">Read more here</a>.</p></td><td><br>You need to notify without requiring a response.</td><td><mark style="color:$info;">Push notifications, system alerts, or status updates sent to users or services.</mark></td></tr><tr><td><h4><strong>RPC</strong></h4><p><img src="/files/h1amQ8mNRw7pLbixAeY0" alt=""></p><p></p></td><td><p>Structured request/response interactions between systems.<br>Ideal for complex operations where the other party may not be online at the time of execution (especially useful for policy-driven workflows).</p><p></p><p>Supported in the <code>SDK</code> semantics.<br><a href="https://docs.atsign.com/tutorials/atsdk-tutorial">Read more here</a>.</p></td><td>You want function-like calls across services with clear inputs and outputs.</td><td><mark style="color:$info;">Calling a billing service to calculate charges or a policy engine to validate access.</mark></td></tr><tr><td><p><strong>Data Stream</strong></p><p><img src="/files/E0RLIWMEPCnGWkwNo5Fn" alt=""></p></td><td>Ideal for long-running processes (such as LLM generation) where completion time is uncertain and results may need to stream incrementally.<br><br>Uses the <code>stream</code> (<a href="https://docs.atsign.com/sdk/events#monitor">Read more here</a>) semantics<br>or <code>notifications</code> (<a href="https://docs.atsign.com/tutorials/atsdk-tutorial/send-and-receive-data-synchronously#sending-and-receiving-data-in-near-real-time">Read more here</a>) semantics.</td><td>You’re handling live, ongoing data rather than discrete messages</td><td><mark style="color:$info;">Telemetry feeds, sensor data, logs, or real-time analytics pipelines.</mark></td></tr><tr><td><p><strong>TCP</strong></p><p><img src="/files/wgtvRAUeSmUkEDPcjD8a" alt=""></p></td><td><p>Low-level, persistent network connections that enable lower latency communication (e.g., NoPorts SSH, MCP servers).</p><p><br>Uses <code>Noports_Core</code> (<a href="https://pub.dev/packages/noports_core">Read more here</a>) semantics.</p></td><td>You need a fast end-to-end encrypted TCP connection with raw network speed.</td><td><mark style="color:$info;">A custom protocol, legacy integration, or long-lived secure connection.</mark></td></tr></tbody></table>


# AI Architect Walkthroughs

Choose a walkthrough to learn how to design, structure, and generate LLM‑ready prompts using AI Architect.

### MCP Walkthrough

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover-dark data-type="image">Cover image (dark)</th></tr></thead><tbody><tr><td><strong>Build with Claude and MCP</strong></td><td>Learn how to connect Claude to the Atsign MCP server and generate blueprints directly from your prompts.</td><td><a href="/files/iQYp4FBDqUr8bAVlw1j8">/files/iQYp4FBDqUr8bAVlw1j8</a></td><td><a href="/pages/oQ37i0lODywIrDxLOVsl">/pages/oQ37i0lODywIrDxLOVsl</a></td><td><a href="/files/pr9poJ5cazsaTwiddIwI">/files/pr9poJ5cazsaTwiddIwI</a></td></tr></tbody></table>

### App Walkthroughs

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th data-hidden data-card-cover data-type="image">Cover image</th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Getting Started with AI Architect</strong> <br><mark style="background-color:violet;">Secure Messaging App</mark><br><br>A quick walkthrough showing how to open the tool, load a sample blueprint, generate a prompt, and use it to build your first app end‑to‑end.</td><td data-object-fit="contain"><a href="/files/iBmTmzopn0odj1pg9E8a">/files/iBmTmzopn0odj1pg9E8a</a></td><td><a href="/pages/kl1BGiv2y2gaaVzPtgK5">/pages/kl1BGiv2y2gaaVzPtgK5</a></td></tr><tr><td><strong>How to Think When Creating a Blueprint</strong> <br><mark style="background-color:green;">Grocery List App</mark><br><br>A focused guide that starts with a sample blueprint and expands it, detailing you how to map your own blueprint so the LLM builds the app you intend.</td><td data-object-fit="contain"><a href="/files/45f5X62TmY346nrvkEy3">/files/45f5X62TmY346nrvkEy3</a></td><td><a href="/pages/BMmxKb8B5KbC9JIbYnut">/pages/BMmxKb8B5KbC9JIbYnut</a></td></tr></tbody></table>


# Build with Claude and MCP

The **Model Context Protocol (MCP)** is a standard that allows AI models like Claude to securely connect to external tools, services, and data sources. In AI Architect, MCP is what enables Claude to communicate with the **Atsign MCP server** so it can generate blueprints, fetch information, and perform actions based on your instructions.

By connecting Claude to the AI Architect MCP server, you give it controlled access to the tools it needs to create your blueprint directly on the canvas.

{% tabs %}
{% tab title="Claude Desktop" %}

### Connect the AI Architect MCP (Claude Desktop)

Follow these steps to connect Claude Desktop to the Atsign AI Architect MCP server.

#### **1. Open Claude Desktop**

Launch the Claude Desktop application on your device.

#### **2. Open the Connectors menu**

In the left sidebar, click **Customize**, then select **Connectors**.

#### **3. Add a new custom connector**

Click the **+** icon and choose **Add Custom Connector**.

#### **4. Enter the connector details**

You will be prompted to configure the connector:

* **Name:** Enter a name such as *AI Architect MCP*
* **Remote MCP Server URL:** Paste the following URL:

```
https://aiarchitect.atsign.com/mcp
```

* Click **Add** to save the connector.

#### **5. (Optional) Set tool permissions**

Claude may ask for permission to use the connector’s tools. Select **Always allow** for a smoother workflow.

#### **6. Start a new chat**

Click **New Chat** in Claude Desktop.

#### **7. Enable the connector**

Inside the new chat:

* Click the **+** icon
* Select **Connectors**
* Toggle on your newly added **AI Architect MCP** connector

#### **8. Copy your Session ID**

A Session ID will be displayed in the setup dialog. Click **Copy** to copy it to your clipboard.

#### **9. Include the Session ID in your prompt**

Paste the Session ID into your prompt to Claude and explicitly mention that you want an **“Atsign AI Architect blueprint”** so Claude knows to use the MCP server.

Example:

{% code overflow="wrap" %}

```
I want to create an AI Architect blueprint for an IT Helpdesk app. My session ID is: stream-FUW1BbuQ9BTT8cxR5Vv65B
```

{% endcode %}

#### **10. Establish the connection**

Click **Establish Connection** to activate the MCP session. Your blueprint will be generated and displayed on the canvas.
{% endtab %}

{% tab title="Claude Browser" %}

### Connect the AI Architect MCP (Claude Browser)

Follow these steps to connect Claude in the browser to the Atsign AI Architect MCP server.

#### **1. Open Claude.ai**

In your browser, open a new tab and go to [**https://claude.ai**](https://claude.ai).

#### **2. Open the Connectors menu**

In the left sidebar, click **Customize**, then select **Connectors**.

#### **3. Add a new custom connector**

Click the **+** icon and choose **Add Custom Connector**.

#### **4. Enter the connector details**

You will be prompted to configure the connector:

* **Name:** Enter a name such as *AI Architect MCP*
* **Remote MCP Server URL:** Paste the following URL:

```
https://aiarchitect.atsign.com/mcp
```

* Click **Add** to save the connector.

#### **5. (Optional) Set tool permissions**

Claude may ask for permission to use the connector’s tools. Select **Always allow** for a smoother workflow.

#### **6. Start a new chat**

Click **New Chat** in Claude Desktop.

#### **7. Enable the connector**

Inside the new chat:

* Click the **+** icon
* Select **Connectors**
* Toggle on your newly added **AI Architect MCP** connector

#### **8. Copy your Session ID**

A Session ID will be displayed in the setup dialog. Click **Copy** to copy it to your clipboard.

#### **9. Include the Session ID in your prompt**

Paste the Session ID into your prompt to Claude and explicitly mention that you want an **“Atsign AI Architect blueprint”** so Claude knows to use the MCP server.

Example:

{% code overflow="wrap" %}

```
I want to create an AI Architect blueprint for an IT Helpdesk app. My session ID is: stream-FUW1BbuQ9BTT8cxR5Vv65B
```

{% endcode %}

#### **10. Establish the connection**

Click **Establish Connection** to activate the MCP session. Your blueprint will be generated and displayed on the canvas.
{% endtab %}
{% endtabs %}

### FAQs

<details>

<summary>What is MCP?</summary>

MCP is a protocol that lets Claude securely connect to external tools. In AI Architect, it allows

</details>

<details>

<summary>Why do I need to connect Claude to the MCP server?</summary>

Connecting Claude to the MCP server gives it access to the tools required to create and update your blueprint automatically.

</details>

<details>

<summary>Where do I find the MCP server URL?</summary>

The URL is shown in the setup dialog inside AI Architect. You’ll paste it into Claude when adding a custom connector.

{% code overflow="wrap" %}

```
https://aiarchitect.atsign.com/mcp
```

{% endcode %}

</details>

<details>

<summary>What is the Session ID for?</summary>

The Session ID links your Claude chat to your active AI Architect session. Claude uses it to generate your blueprint in the correct workspace.

</details>


# Getting Started with AI Architect

This walkthrough guides you through loading a Blueprint and exporting a prompt using AI Architect, then using that blueprint to generate and run an Atsign Platform app in your IDE.

{% stepper %}
{% step %}

### [Open AI Architect and Load the Example Blueprint](#open-ai-architect) <a href="#open" id="open"></a>

Load the example so you can see how a Blueprint is structured.
{% endstep %}

{% step %}

### [Export the Prompt](#id-2.-export-the-prompt)

Export the prompt that will be used to tell you LLM exactly what to build.
{% endstep %}

{% step %}

### [Open Your IDE, Plan and Code your App](#id-3.-open-your-ide-plan-and-code-your-app)

Open your IDE, set your LLM to plan, and once happy, let it generate the code.
{% endstep %}

{% step %}

### [Build and Run the App](#id-4.-build-and-run-the-app)

Run the app to make sure everything works as expected.
{% endstep %}

{% step %}

### [Test the App with Your Atsigns](#test-the-app-with-your-atsigns)

Authenticate using your Atsigns and test the functionality
{% endstep %}

{% step %}

### [Create Your Own Blueprint](#id-5.-create-your-own-blueprint)

Make a Blueprint for your own idea and repeat the process.
{% endstep %}
{% endstepper %}

***

### 1. Open AI Architect and Load the Example Blueprint <a href="#open-ai-architect" id="open-ai-architect"></a>

AI Architect is the visual blueprinting tool used to design your app’s structure before generating the LLM prompt.

1. Go to [**aiarchitect.atsign.com**](https://aiarchitect.atsign.com/)  and enter your email to sign in or sign up. You’ll receive a magic link in your inbox. Click it to access the tool. Once you're in, the workspace will open where you can create a new Blueprint or load an existing one.&#x20;
2. Click on **Start with Example Blueprint** and select the Secure Messaging Blueprint to load our prebuilt example Blueprint. AI Architect will populate the canvas.

{% hint style="info" %}
A Blueprint is a visual map of your application. Each box represents a node. This could be a person, a process, an AI agent, a service, or any other entity involved in your system. The lines between nodes show how information flows from one part of the system to another.

Every node includes a Notes section, which acts as the node’s job description. This is where you define:

* what the node is responsible for
* what information it needs
* how it behaves

A clear Blueprint gives the LLM the structure it needs to build your application in stages.
{% endhint %}

### 2. Export the Prompt

Once the blueprint is ready:

1. Select **Export  Prompt.**
2. Copy the generated prompt to your clipboard.

Export Guide generates the full LLM prompt based on your blueprint.&#x20;

{% hint style="info" %}
The prompt includes:

* A high‑level application description
* A breakdown of all nodes (people, processes, things)
* A breakdown of all connections and their types
* Atsign Platform roles for each component
* Implementation notes for each node
* Stream/notification patterns for each connection
* Required dependencies and initialization code
* Authentication setup
* A step‑by‑step implementation guide

This becomes the instruction set your LLM will follow to generate the full application.
{% endhint %}

### 3. Open Your IDE, Plan and Code your App

{% hint style="success" %}
You can use any IDE you like but we recommend [Visual Studio Code](https://code.visualstudio.com/). When selecting LLMs we have had the most success with the following:

* Claude Sonnet 4.5+
* Claude Opus 4.5+
  {% endhint %}

Visual Studio Code gives you the flexibility to work with a variety of LLMs, not just one. Depending on the extensions you install, you can choose from models like OpenAI, Gemini, Claude and others.&#x20;

To get started, you’ll need to create a new empty folder and set your LLM to **Plan Mode.** Starting in Plan Mode is important because it helps to ensure the LLM is going to build what you want it to build.

1. Paste the exported prompt directly into the chat window. It will plan the project and present the plan to you. When you are happy, Proceed with implementation and it will create files, and set up the app.&#x20;
2. You will be asked to confirm certain actions (file creation, folder setup, dependency installation).
3. The LLM will build a [**Dart and Flutter app**](https://docs.flutter.dev/learn/pathway/quick-install). It may build the application in stages, allowing you to test each step and provide additional instructions. It will continue refining and completing the app based on your original prompt as you guide it through each iteration.

{% hint style="info" %}
The LLM will:

* Parse the entire blueprint
* Create the full folder structure
* Generate Dart/Flutter code using at\_client and related packages
* Implement each node as a module or service
* Implement each connection using streams or notifications
* Set up authentication, onboarding, and identity management&#x20;
  {% endhint %}

### 4. Build and Run the App

Once the code generation is complete:

1. Follow the build instructions created by your LLM.
2. Run the app on two separate devices/simulators/emulators.

### 5. Test the App with your Atsigns

To test your app, you’ll need two atSigns. These serve as the identities your app uses during testing. When prompted in your app, you can claim your free starter pack Atsigns either inside the app or through your browser.

1. Visit [**my.atsign.com/starterpack**](https://my.atsign.com/starterpack). If you already have a starter pack, you can log into [**my.atsign.com/login**](https://my.atsign.com/login) to access the Atsigns.
2. Verify your email and claim your two free Atsigns.
3. In your app, go to the authentication screen and choose to activate or create a new Atsign.
4. You’ll receive a one‑time password by email to complete activation.
5. After activation, you’ll be asked to save your atKeys (your cryptographic keys).
6. Try the app by sending messgaes from one device to another.

{% hint style="info" %}
This validates:

* Atsign onboarding
* Secure key management
* Encrypted messaging
  {% endhint %}

### 6. Create your own Blueprint

Once you’ve explored the example Blueprint, you’re ready to create your own. Start Simple. A Blueprint doesn’t need to be perfect on the first pass. Its purpose is to help you think clearly about how your application works and to get you to working code quickly and securely.

When designing your Blueprint, focus on three core questions:

1. What are the nodes?
2. What does each node do?
3. How does information move between them?

These three decisions form the foundation of your application’s architecture and guide the LLM as it builds and refines your app.&#x20;

For help with this follow our walkthrough on [**How to Think When Creating a Blueprint**](/atsign-ai-architect/ai-architect-walkthroughs/how-to-think-when-creating-a-blueprint)**.**

### Support and Further Help

*If you run into issues, have questions about any step, or want to go deeper into building with Atsign Platform, the Atsign team can help. Contact <support.team@atsign.com>.*


# How to Think When Creating a Blueprint

This tutorial walks you through growing a simple grocery list app into something useful with AI Architect.

### Introduction

When you open this starter blueprint in AI Architect, you’re looking at something intentionally simple: Two people share a grocery list. One adds items, and the other sees them.

While this works as a basic concept, a blueprint needs more depth to generate high-quality functional code. This tutorial teaches you how to look at a simple blueprint and extend it in a way that improves the **data flows**, **actor responsibilities**, and **system behaviors**. These are the three things AI Architect relies on most when generating applications.

#### Perspective shift

Before you begin, it is helpful to shift your focus from adding *features* to identifying the **behaviors** required by your app's data flows.

### What you’ll learn

* What to consider when thinking about actors, data, and flows
* How to extend a blueprint in a way that AI Architect can make use of
* How to identify missing behaviors, not just missing features

### Starter blueprint

To get started, go to [**aiarchitect.atsign.com**](https://www.aiarchitect.atsign.com/). From the home screen, select **Start with an Example Blueprint**, then choose the **Grocery List App** blueprint to load the example.

Page 1, is the starting point. Before you change anything, let's take a moment to understand the actors, data, and flows.

{% tabs %}
{% tab title="Starter Blueprint" %}

<figure><picture><source srcset="/files/oR3nUo0UIzNsNv5XkIKi" media="(prefers-color-scheme: dark)"><img src="/files/R5xuU6qeO9TWS3YJBR9v" alt=""></picture><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}

### 1. Start with the people (actors)

At the core of this app are two actors: the **creator** (who adds items) and the **viewer** (who sees them).

{% hint style="success" %}
**Goal**\
\
Understand the roles before adding complexity.
{% endhint %}

{% hint style="info" %}
**Consider**

* Who creates data?
* Who consumes it?
* Does each actor have a clear purpose?
  {% endhint %}

{% hint style="warning" icon="comments-question-check" %}
**Why it matters**\
\
AI Architect is actor-driven. If an actor doesn’t meaningfully create, transform, or act on data, the generated application will feel passive or incomplete.
{% endhint %}

### 2. Understand the data

The core data objects are the **grocery list** and the **list item**. These are the “source of truth” for the entire app.

{% hint style="success" %}
**Goal**\
\
Map the data that exists independently of the UI.
{% endhint %}

{% hint style="info" %}
**Consider**

* What data would exist even without a screen?
* What data connects actors to each other?
  {% endhint %}

{% hint style="warning" icon="comments-question-check" %}
**Why it matters**\
\
AI Architect depends on clear, well‑defined data objects. If the core data objects aren’t clearly defined, the generated application may not behave the way you expect.
{% endhint %}

### 3. Follow the flow

Right now the flow is linear:

Creator → List item → Grocery list → Viewer

{% hint style="success" %}
**Goal**\
\
Establish your baseline.
{% endhint %}

{% hint style="info" %}
**Consider**

* What happens first, next, and last?
* Where does the app stop being helpful to the actors?
  {% endhint %}

{% hint style="warning" icon="comments-question-check" %}
**Why it matters**\
\
Seeing the sequence helps you spot where the chain of communication breaks. When you understand how information moves, it's easier to identify missing behaviors and data flows.&#x20;
{% endhint %}

### 4. Make the app more responsive

At this stage, the viewer has to check the app manually to see updates. That’s friction we want to remove.

{% hint style="success" %}
**Goal**

* Turn a static display into a proactive system.
  {% endhint %}

{% hint style="danger" icon="message-check" %}
**Action**

* Add a **notification node**
* Create a flow: Grocery List → Notification → Viewer
  {% endhint %}

{% hint style="info" %}
**Consider**

* Where is the person doing unnecessary work?
* What should the app proactively communicate?
  {% endhint %}

{% hint style="warning" icon="comments-question-check" %}
**Why it matters**\
\
AI Architect needs to know when the app should act on its own. Specifying that certain changes should trigger actions allows you to generate a responsive app instead of something static.
{% endhint %}

### 5. Turn observers into participants

The viewer can see the list but can’t interact with it. Let's give them a more active role.

{% hint style="success" %}
**Goal**

* Enable interaction for every actor on the canvas.
  {% endhint %}

{% hint style="danger" icon="message-check" %}
**Action**

* Add **item status** to the notes section of the **list item** node.
* Update the viewer and creator notes to include the ability to update the status of an item in the list or mark it as completed.
  {% endhint %}

{% hint style="info" %}
**Consider**

* What data should this actor be allowed to update?
* Who, if anyone, should grant them permission to update data?
  {% endhint %}

{% hint style="warning" icon="comments-question-check" %}
**Why it matters**\
\
AI Architect needs to know how each actor meaningfully interacts with the data. When someone only observes information, the app has no behavior to generate around them. Turning an observer into a participant, like letting the Viewer update item status, is a concrete example of using the existing data to extend functionality. By giving the actor a real action on the data, you unlock new flows, richer behavior, and a more complete application.
{% endhint %}

### 6. Introduce intelligence

Once the core flows are solid, you can add intelligence to transform data into something more useful.

{% hint style="success" %}
**Goal**

* Provide a structured home for AI logic.
  {% endhint %}

{% hint style="danger" icon="message-check" %}
**Action**&#x20;

* Add a **suggestion agent** and **suggested items**
  * Flow: Grocery List App → Suggested items → Suggestion Agent&#x20;
  * You can also add Suggested Items → Notification for reminders and alerts if you'd like
    {% endhint %}

{% hint style="info" %}
**Consider**

* Is there a pattern in the data?
* Would a human naturally make suggestions here?
* What other parts of the app need to feed into this data flow for it to be complete?
  {% endhint %}

{% hint style="warning" icon="comments-question-check" %}
**Why it matters**\
\
Adding an agent isn’t just about making the app “smarter"; it’s about giving the system a clear place to transform data into something more useful. When you introduce intelligence, like a Personal AI agent and suggested items,  you’re showing how the app can use existing data to extend functionality in a purposeful, predictable way. Without this structure, the LLM has nothing reliable to build intelligent behavior around.
{% endhint %}

### 7. Add descriptions

Think of the description section as the node's job description. This is the most critical step for code generation.&#x20;

{% hint style="success" %}
**Goal**&#x20;

Provide the manual for the LLM to follow.&#x20;
{% endhint %}

{% hint style="danger" icon="message-check" %}
**Action**

For each node:

* Write its purpose, what data it can access, and how it interacts with other nodes
  * Use natural language or bullet points, as if you were explaining to a developer exactly what you expect this node to do.
    {% endhint %}

{% hint style="info" %}
**Consider**

* What data does this node own?
* Who can read or write it?
* What else is it used for in the app?
  {% endhint %}

{% hint style="warning" icon="comments-question-check" %}
**Why it matters**\
\
AI Architect depends heavily on the description. Descriptions tell us what the node knows, what data it owns, and how it behaves. Without clear descriptions, the blueprint may look complete, but the generated app won’t know what to do. This can lead to missing logic or incorrect flows. Adding descriptions is how you turn a diagram into a blueprint that an LLM can actually build from.
{% endhint %}

### 8. Define connection types

Now, define how the nodes interact to complete the loop.

{% hint style="success" %}
**Goal**

Tell the system the speed and method of communication. To learn more about the various connection types, see the [Nodes & Connections](/atsign-ai-architect/atsign-ai-architect-overview/nodes-and-connections) page.&#x20;
{% endhint %}

{% hint style="danger" icon="message-check" %}
**Action**

Connect each of the nodes so that data can flow through the system in a complete loop. Every component should participate in the app’s behavior, and no node should be left without at least one incoming or outgoing connection.

\
Add/Modify the following connections as:

* Grocery List App > Notification (Notifications)
* Notification > Viewer (Notifications)
* Notification > Creator (Notifications)
* Notification > Suggestion Agent (Notifications)
  {% endhint %}

{% hint style="info" %}
**Consider**\
\
Is this interaction immediate, background, or interruptive? If you are unsure, you can set it to **data stream**.
{% endhint %}

{% hint style="warning" icon="comments-question-check" %}
**Why it matters**\
\
AI Architect relies on connection types to understand *how* data should move through the system. These labels tell the platform whether something happens immediately, in the background, or as a notification. If you’re unsure which one to choose, it’s completely fine to use the **default** connection type. The default connection type simply says, “this data may update over time,” and lets the LLM decide how to handle it, which is a safe, flexible choice. You can refine the connection later, but starting with a default connection ensures the system behaves reliably even when the exact timing isn’t clear yet.
{% endhint %}

### Final Blueprint

That's it! The completed blueprint should represent a full loop of interaction where every component participates in the app's behavior. If you go to page 2 of the Grocery List App blueprint, you will see the final blueprint.

{% tabs %}
{% tab title="Final Blueprint" %}

<figure><picture><source srcset="/files/N1KC8INNidohSEo0NN9I" media="(prefers-color-scheme: dark)"><img src="/files/oVpuazfTaWU4EJdX6cOs" alt=""></picture><figcaption></figcaption></figure>
{% endtab %}

{% tab title="Starter Blueprint" %}

<figure><picture><source srcset="/files/oR3nUo0UIzNsNv5XkIKi" media="(prefers-color-scheme: dark)"><img src="/files/R5xuU6qeO9TWS3YJBR9v" alt=""></picture><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}

### App Prototype

Now that your blueprint is complete, you’re ready to generate your app prototype using an LLM. To do that, follow the instructions on the [Getting Started with AI Architect](https://docs.atsign.com/tutorials/ai-architect-walkthrough#id-2.-export-the-prompt) page, starting from **Step 2**.&#x20;

{% tabs %}
{% tab title="App Screenshots" %}
&#x20;<img src="/files/45f5X62TmY346nrvkEy3" alt="" data-size="original">![](/files/azW0dFCFpLqsXuqhIbc5)
{% endtab %}
{% endtabs %}

### Common Mistakes

<details>

<summary><strong>Adding actors who don’t actually do anything</strong></summary>

If an actor has no meaningful actions on the data, the system becomes passive and AI Architect has nothing to generate around them.

</details>

<details>

<summary><strong>Skipping the data layer</strong></summary>

People jump straight to features or agents without defining the core data objects. Without clear data, the entire blueprint becomes unstable.

</details>

<details>

<summary><strong>Treating all data as one blob</strong></summary>

Not separating structures, like a Grocery List vs. List Item, leads to vague flows and confusing behavior.

</details>

<details>

<summary><strong>Ignoring the flow of information</strong></summary>

If you don’t map out who sends what to whom, the system feels unpredictable. AI Architect needs explicit flows to generate correct behavior.

</details>

<details>

<summary><strong>Forgetting to make the system responsive</strong></summary>

If nothing triggers actions, notifications, updates, or background work, the app may feel static and unhelpful and these will have to be added in later.

</details>

<details>

<summary><strong>Leaving observers as observers</strong></summary>

When someone only views data, the system stays limited. In some instances this is okay but using the data to extend functionality (like letting the Viewer update item status) unlocks richer behavior

</details>

<details>

<summary><strong>Writing vague or empty notes</strong></summary>

Nodes without notes force AI Architect to guess. Notes define purpose, ownership, and behavior.

</details>

<details>

<summary><strong>Not labeling connection types</strong></summary>

If you don’t specify sync, async, or notification, the system can’t generate the right timing or behavior. (And yes, if you’re unsure, defaulting to a data stream is perfectly fine.)

</details>

<details>

<summary><strong>Over‑engineering the blueprint</strong></summary>

Too many nodes, too many agents, too many flows. Complexity without purpose makes the system harder to understand and harder for AI Architect to generate.

</details>


# Release Notes

This page summarizes the latest features and bug fixes to help you understand what has changed in each release.

***

{% updates format="full" %}
{% update date="2026-07-08" %}

## Live walkthroughs & 7-day sign-in

<sup>v3.0.30</sup>

<mark style="color:$primary;background-color:orange;">**Schedule a call with us**</mark>

* You can now book a kickstart session with our team to walk through a demo or get guided support building your first blueprint.

<mark style="color:$primary;background-color:orange;">**Extended session lengths**</mark>

* We’ve extended the auto sign-out window from 24 hours to 7 days so you can pick up exactly where you left off without the daily sign-in hassle.
  {% endupdate %}

{% update date="2026-06-22" %}

## Improved performance

<sup>v3.0.25</sup>

<mark style="color:$primary;background-color:orange;">**Faster and smoother experience**</mark>

* Sign‑in is now faster, blueprint editing feels smoother, and moving **nodes** around the canvas is more responsive, giving you a noticeably quicker and more fluid experience end‑to‑end.
  {% endupdate %}

{% update date="2026-06-08" %}

## Build with Claude, powered by the Atsign MCP server

<sup>v3.0.0</sup>

<mark style="color:$primary;background-color:orange;">**Create blueprints faster, with Claude**</mark>

* You can now create complete blueprints using your **Atsign MCP server** and **Claude**, giving you a new, streamlined way to design secure architectures
* Create, refine, and iterate on blueprints without touching the UI. Perfect for fast prototyping or automated workflows

<mark style="color:$primary;background-color:orange;">**Expanded testing capabilities**</mark>

* Trial now includes **10 free Atsigns** (up from 2) so you can test and prototype more freely
* Improved export‑prompt UI for a clearer, faster workflow

<mark style="color:$primary;background-color:orange;">**Better sign-up and support**</mark>

* Updated sign‑up flow with lightweight KYC to help us understand your needs and provide faster, more accurate support
  {% endupdate %}

{% update date="2026-05-06" tags="2.0.1" %}

## More ways to build, less friction

<sup>v2.0.0</sup>

<mark style="color:$primary;background-color:orange;">**New nodes, smarter flows**</mark>

* Introducing the **AI Agent node**, giving you more flexibility to design intelligent workflows
* Let the LLM take the lead with **default connections**, so you can focus on mapping your blueprint instead of configuring every step

<mark style="color:$primary;background-color:orange;">**Account experience**</mark>

* Added a **magic link flow** for a seamless account access
* New **sign‑up and sign‑in flow**, added as the first step toward a more secure, personalized experience
* Subscribe to email updates under **Account Details** to stay up to date with the latest improvements

<mark style="color:$primary;background-color:orange;">**Get started quicker**</mark>

* Reduced early friction by allowing you to sign up and receive your test Atsigns to the end of the flow, only once your app is built.

{% hint style="info" icon="lightbulb" %}
*New example blueprint!*&#x20;

Jump into the [**Grocery List blueprint**](/atsign-ai-architect/ai-architect-walkthroughs/how-to-think-when-creating-a-blueprint) to quickly see how to structure and connect your ideas.
{% endhint %}
{% endupdate %}

{% update date="2026-04-06" tags="1.0.12" %}

## More room to build

<sup>v1.1.3</sup>

<mark style="color:$primary;background-color:orange;">**More canvas, less menu**</mark>

* The side navigation now collapses to make the canvas center stage
* You can also hide the mini-map to maximize your workspace

<mark style="color:$primary;background-color:orange;">**Just connect, change later**</mark>

* Connectors now default to **data stream** out of the box
* You can update the connector type at any time after the initial connection

{% hint style="info" icon="lightbulb" %}
*Not sure what a button does? Hover over it for a hint.*
{% endhint %}
{% endupdate %}
{% endupdates %}


