Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Core Technology
The atSDK
The format atServers use to store and share data.
atRecords are the data records that are stored by the atServers. We use the common key-value pair format.
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.
A marker for whether it is a cached key or not.
A marker for who has access to the key.
A unique string used to represent the atRecord.
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.
Metadata of the atRecord is also stored and describes the following properties of the atValue.
Text or binary values can be saved in an atServer.
Small objects are fine to use the atServer but large objects should be used by reference.
For example, derive a new encryption key, encypt a file, upload that file to location, then notify other atSigns of the location and the encyption key. This is how works.
The size of the value saved in an atServer is bound by the atProtocol's config parameter "maxBufferSize".
An overview of Atsign's core pillars of technology
The atPlatform allows people, entities and things to communicate privately and securely without having to know about the intricacies of the underlying IP network. The atProtocol 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.
The atPlatform 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.
It takes two to tango!
To run through the tutorial you will need at least two atSigns, so you can send and receive. You can get your atSigns for free at or if you like purchase some that are more personal to you.
Once you have you atsigns you are ready to activate them, which means spinning up your atServer per atSign and cutting your cryptographic keys for each atSign. Sounds complicated but it is easy ! In fact just a single command. In the terminal window type:
You will get an error message telling you to add the -a flag and your atsign, so if you atSign was @crunchfrog you would type:
*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. Talking of data lets send some between the two atSigns next.
Allowed characters in an entity are: [\w._,-"']
Namespace is mandatory in the current implementation of the protocol
i.e entity must follow the notation: <identifier>.<namespace>
Cached atKeys should have a different owner than the current atSign
Visibility scope and owner cannot be the same for a shared atKey
For newly created atKeys, the owner must match the current atSign
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
A public atKey with a record id of publickey shared by @bob. Note that this is a reserved atKey.
public:publickey@bob
Private atKey
A private atKey with a record id of pk1 shared by @alice.
privatekey:pk1@alice
Shared atKey
A shared atKey with a record id of phone shared with @bob, shared by @alice.
@bob:phone@alice
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
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
A cached atKey with a record id of phone, shared with @bob, and is shared with @alice.
cached:@bob:phone@alice
Date and time when the key has been created.
expiresOn
Yes
A Date and Time derived from the ttl (now + ttl). A Key should be auto deleted once it expires.
isBinary
No
True if the value is a binary value.
isCached
No
True if the key is cached.
isEncrypted
No
True if the value is encrypted.
refreshAt
No
A Date and Time derived from the ttr. The time at which the key gets refreshed.
sharedWith
No
atSign of the user with whom the key has been shared. Can be null if not shared with anyone.
updatedOn
Yes
Date and time when the key has been last updated.
ttb
No
Time to birth in milliseconds.
ttl
No
Time to live in milliseconds.
ttr
No
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).
Public
Store and share public data which can be seen by anyone.
Self
Store data which can only be seen by the owner.
Shared
Store and share private data which can only be seen by the owner and intended recipient.
Private
Store data which can only be seen by the owner, hidden by default.
Cached
Cache shared data from other atSigns for performance and offline mode.
Meta Attribute
Auto create?
Description
availableFrom
Yes
A Date and Time derived from the ttb (now + ttb). A Key should be only available after availableFrom.
ccd
No
Indicates if a cached key needs to be deleted when the atSign user who has originally shared it deletes it.
createdBy
Yes
atSign that has created the key
createdOn
Yes
dart run .\bin\at_activate.dart dart run .\bin\at_activate.dart -a "@crunchyfrog"
[cached:]<visibility scope>:<record ID><owner’s atSign>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 atProtocol is the application layer protocol used to communicate with an 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.
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.
The atProtocol is an application protocol that enables data sharing between atSigns. You can learn more about the atProtocol by reading the . The atProtocol uses TCP/IP and TLS but does not specify how data itself is encrypted, that is the job of the atSDK and atClient libraries.
atSDKs provide developers with atPlatform 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 the atPlatform.


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.
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.
Head to the registrar site. It is recommended that you login with your email. One email can hold up to 10 free atSigns and unlimited paid atSigns.
There are multiple ways to generate the associated .atKeys file for an atSign. Within applications the .atKeys are stored in encrypted keychains that the OS provides. Using the command line .atKeys files are produced and put in the directory ~/.atsign/keys.
Most applications have a way to export the .atkeys to a file if you need to keep them safe or use them on another device.
Using atmospherePro (3 minute video)
Dart at_onboarding_cli/at_activate to activate an owned atSign or at_onboarding_cli/at_register to generate a new free atSign.
You can purchase custom atSigns from the registrar site.
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.
To trigger a sync, call the .sync function:
AtClientManager atClientManager = AtClientManager.getInstance();
SyncService syncService = atClientManager.syncService;
syncService.sync();Send me a txt!
Sending data as we saw 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:
atClient.notificationService
.subscribe(regex: 'message.$nameSpace@', shouldDecrypt: true)
.listen(((notification)And to send code like this:
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 an receive synchronously.
To test for yourself you will need two terminal windows to do that you can press the split button on the VS Code IDE.
You can then run the following commands agian note to replace the atSigns with your own.
In the right window run this first to receive:
In the left window you can now send a message using:
The net result should look like this, and your message should appear in the right-hand side.
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.
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.
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.
Offline support for apps
Flutter / Dart
Control the connection lifecycle
C
How we scale and provide resilience.
The atPlatform 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 atPlatform Infrastructure.
Atsign services are monitored by an independent third party for uptime and can be seen here:
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.
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.
spooky actions at a distance
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!)
This pulls in needed libraries, and then this starts the RPC listener.
The atPlaform and the atSDKs are all open souce and the code can be found on GitHub and downloaded from . But we will start with some easy sample code to show how data can be shared and notifications can be sent between atSigns. These are the basic building blocks for powerful applications like and graphical applications like , both of which are also opensource so you can see the code in action.
Some very simple example of using the atSDK to get a feel for what it offers and create building blocks for your larger projects.
Dart is an opensource project from Google offering a fast and multi-platform programming language. The atPlatform team choose Dart as a high level language to build proof of concept code but 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.
Setup the atSDK for your preferred language
How to authenticate to an atServer
Learn how to create atKeys for your chosen platform
How to do basic CRUD operations on an atServer
How to send and receive real-time messages
Implementation specific features to know about


dart run .\bin\at_notify_receive.dart -a "@energetic22" -n "atsign"dart run .\bin\at_notify_send.dart -a "@7capricorn" -n "atsign" -o "@energetic22" -m "hello world"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, 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.
Each atSign has its own dedicated personal data store, called an "atServer," running as a Docker container within a Docker Swarm. Atsign runs a number of Docker Swarms and can move atServers from one swarm to another. However, for high availability, Atsign relies 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.
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.
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.


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 send to the other atSign the answer calculated and then returned.
See our session in action:
Couple of things to notice here are that the RPC server will only respond to RPC's 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.
An AtRpc object is set up pretty simply with:
The Callbacks are sent to the DemoRpcServerclass and handled appropriately.
On the sending side the RPC client is initiated:
Then the RPC simple sent and awaiting a reply
dart pub getdart pub get
dart run .\bin\arithmetic_server.dart -a "@energetic22" -n "atsign" --allow-list "@7capricorn"In VS Code you can click "Clone Git Repository" and enter the URL
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.
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.
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.
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.
Dart is available at dart.dev 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 as it has excellent support for Dart, download install VS Code then install the Dart extension.
You have mail!
The next step is to send and receive for this to work we need to get the atKeys file containing the cryptographic keys and login 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 owners 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:
await atClient.put(sharedRecordID, message, putRequestOptions: pro);of course we also have to setup the atClient object and onboard but that is all in the at_key_put.dart file for you. so let's send some data.
Running the program without arguments brings up this help, most is self explanatory.
*Note here we are using the atSigns activated in the previous section just substitute your own atSigns.
The -nnamespace 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 yourself!
You may also notice yourself 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.
The code is worth taking a look at the code as it less than 70 lines but allows data to transferred from a device anywhere to a device anywhere, and the data is fully encrypted.
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.
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, its 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. The enter:
Like before, you will get asked if you want to run pub get and say yes or if you prefer you can open a terminal window and type:
This as we know pulls in the needed libraries and the code will loose 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\arithmetic_client.dart -a "@7capricorn" -n "atsign" --server-atsign "@energetic22" 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);
} var rpc = AtRpcClient(
atClient: atClient,
baseNameSpace: atClient.getPreferences()!.namespace!,
domainNameSpace: 'at_rpc_arithmetic_demo',
serverAtsign: serverAtsign);var response = await rpc.call({'expr': expr}).timeout(Duration(seconds: 5));https://github.com/atsign-foundation/at_demos.gitdart pub getdart pub update




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"
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. ..metadata = (Metadata()
..ttl = 60000 // expire after 60 seconds
..ttr = -1); // allow recipient to keep a cached copyFrom 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!
In the right window, the same but in reverse.
In this session you can see the typed messages in white and th received messages in green with the prompts in red.
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 you can compile the code to a binary using:
Now you can "pipe" the output of a command into the chat with the other atSign.
This prints the code of at_talk.dart into the receiving atsigns chat window. To our knowledge the is the only chat application that you can pipe things too!
If you go this far first thank you and second, please enjoy your continuing journey and raise issues and PR's to any of the repos!
https://github.com/atsign-foundation/at_talk.git


The full example code can be found on our GitHub.
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).
Since not every connection uses hooks, you must explicitly enable it.
Write a function that we would like our atclient connection to run at certain times during the program.
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.
The full example code can be found on our .
dart pub getdart 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. dart run .\bin\at_talk.dart -a "@7capricorn" -t "@energetic22" -n "atsign" dart run .\bin\at_talk.dart -a "@energetic22" -t "@7capricorn" -n "atsign"dart compile exe -o attalk bin/at_talk.dartcat .\bin\at_talk.dart| attalk -a "@energetic22" -t "@7capricorn" -n "atsign"
atclient_connection_hooks_enable(&(atclient->atserver_connection));void *pre_write_hook(atclient_connection_hook_params *params)
{
atlogger_log(TAG, ATLOGGER_LOGGING_LEVEL_INFO, "pre_write_hook called\n");
return NULL;
}atclient_connection_hooks_set(&(atclient->atserver_connection), ATCLIENT_CONNECTION_HOOK_TYPE_PRE_WRITE, pre_write_hook))#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(¬ify_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(¬ify_params, &atkey)) != 0)
{
goto exit;
}
const char *value = "Hello! This is a notification!";
if ((exit_code = atclient_notify_params_set_value(¬ify_params, value)) != 0)
{
goto exit;
}
if ((exit_code = atclient_notify(&atclient1, ¬ify_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(¬ify_params);
return exit_code;
}
}// TODOCreate a Flutter application initialized for use with the atSDK
If you don't have a Flutter setup for development yet, please see the Flutter docs.
1. Create a new Flutter app
First, generate a new Flutter app (replace at_app with the name of your application).
Then add the following dependencies to your project:
Now copy the contents of main.dart from below into lib/main.dart into your project.
Create a second file in lib/ called home_screen.dart. Copy the contents of home_screen.dart from below into your project.
Add the following line to the top of .gitignore:
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:
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:
Start your application with the following command:
Now you are ready to begin developing!
Setup a CMake project which includes the atSDK package suite.
The full example can be found here.
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 or . We recommend because that is currently what Atsign is focused on supporting.
Secondly, ensure you have 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 [email protected]
Lastly, ensure you have a automation tool installed, such as . We recommend because that is currently what Atsign is focused on supporting. These examples are using GNU Make version 4.3, but any version should work.
This command will create a directory and then go into it.
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) .
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.
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"
In the terminal, run the following command.
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:
Use CMake to build your configure and build your programs:
This command is the same as running cd build && make all (if you are using Makefiles).
Your output will look similar to:
The previous command, if ran successfully, would have created a binary executable for you: ./build/main.
Simply run the program:
Your output will look similar to:
Congratulations! You have successfully ran a barebones Atsign C application.
flutter create at_app
cd at_appflutter pub add at_client_mobile at_onboarding_flutter flutter_dotenv path_providerimport '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()}')
]),
),
);
}
}
.envNAMESPACE=
API_KEY=...
flutter:
...
assets:
- .env
...
...flutter rungcc --versioncmake --versionmake --versionmkdir my_first_c_app && cd my_first_c_app# 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)#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;
}
}cmake -S . -B build$ 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/buildcmake --build buildcmake --build build
[ 50%] Building C object CMakeFiles/main.dir/main.c.o
[100%] Linking C executable main
[100%] Built target main./build/main[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!How to authenticate to an atServer
In Dart we provide the package which handles onboarding to the atServer via files stored in the ~/.atsign/keys directory
Add the package to your project automatically using pub:
Set up the to onboard to the atServer.
Next get the onboardingService
Finally wait to be onboarded, this returns true once complete.
This can be wrapped to check that the onboard was successful with the code snippet below.
In Flutter, we provide the at_onboarding_flutter package which handles secure management of these secret keys.
Add the package to your project automatically using pub:
Simply call the onboard method whenever you want your app to open the onboarding widget.
To get an API key for your app, first head to the registrar website, then click manage under one of your atSigns:
Then open the Advanced settings drop down and click Generate New API Key:
#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.
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.
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.
First, include 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.
Finally, call the atclient_utils_populate_atkeys_from_homedir function which will look for your ATSIGN's keys in your $HOME/.atsign/keys/ directory.
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.
Don't forget to run atclient_atkeys_freeat 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.
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.
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).
Thirdly, call the atclient_pkam_authenticate function.
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.
Lastly, do not forget to call atclient_free at the end of your application.
Here is an example application that authenticates my atSign @jeremy_0.
This code is also available on our GitHub.
dart pub add at_onboarding_cli 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);Learn how to create atKeys for your chosen platform
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.
AtOnboardingService onboardingService = AtOnboardingServiceImpl(
fromAtsign, atOnboardingConfig,
atServiceFactory: atServiceFactory);await onboardingService.authenticate();
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');flutter pub add at_onboarding_flutterAtOnboardingResult onboardingResult = await AtOnboarding.onboard(
context: context,
config: AtOnboardingConfig(
atClientPreference: futurePreference,
rootEnvironment: RootEnvironment.Production,
appAPIKey: dotenv.env['API_KEY'],
domain: 'root.atsign.org',
),
);#include <atclient/atclient_utils.h>
#include <atclient/constants.h>char *atserver_host = NULL;
int atserver_port = 0;if(atclient_utils_find_atserver_address(
ATCLIENT_ATDIRECTORY_PRODUCTION_HOST,
ATCLIENT_ATDIRECTORY_PRODUCTION_PORT,
ATSIGN,
&atserver_host,
&atserver_port) != 0) {
// an error occurred
}free(atserver_host);#include <atclient/atkeys.h>atclient_atkeys atkeys;
atclient_atkeys_init(&atkeys);if (atclient_utils_populate_atkeys_from_homedir(
&atkeys,
ATSIGN) != 0)
{
// an error occurred
}atclient_atkeys_free(&atkeys);#include <atclient/atclient.h>atclient atclient;
atclient_init(&atclient);```
if (atclient_pkam_authenticate(
&atclient,
atserver_host,
atserver_port,
&atkeys,
ATSIGN) != 0)
{
// error occurred
}
```atclient_free(&atclient);#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;
}
}

If you are unfamiliar with atKeys please read this first.
The 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:
See below for how to create the various types of atKeys.
To create a public atKey, first use the AtKey.public builder to configure it, then call .build to create it.
The build method on PublicKeyBuilder takes no parameters.
Example
public:phone.wavi@alice
To create a self atkeyD, first use the AtKey.self builder to configure it, then call .build to create it.
The build method on SelfKeyBuilder takes no parameters.
Example
phone.wavi@alice
To create a shared atKey, first use the AtKey.shared builder to configure it, then call .build to create it.
The build method on SharedKeyBuilder takes no parameters.
Example
@bob:phone.wavi@alice
Caching shared atKeys
To cache a shared atKey, you can do a call on SharedKeyBuilder.cache.
You can find the API reference for the entire package available on .
The AtKey class API reference is available .
Public atKey holds public (and non-encrypted) data, available for any atSign to get from you.
Self atKey holds self encrypted data, only available for your own atSign to get.
Shared atKey holds encrypted data that is only decipherable by you and the intended recipient.
Every atKey has 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 on metadata to find out which metadata is worth your time handling.
Before running any of the examples, be sure to include atkey.h
This is how you create a Public atKey.
First, create the atkey struct.
Next, call the atclient_atkey_create_public_key function.
Don't forget to call atclient_atkey_free at the end of the intended life-time of your atkey struct.
This is how you create a Self atKey.
First, create the atclient_atkey struct
Next, call the atclient_atkey_create_self_key function.
Don't forget to call atclient_atkey_free at the end of the intended life-time of your atkey struct.
This is how you create a Shared atKey.
First, create the atclient_atkey struct.
Next, call the atclient_atkey_create_shared_key function.
Don't forget to call atclient_atkey_free at the end of the intended life-time of your atkey struct.
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.
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.
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.
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.
x
flutter pub add at_commonsstatic PublicKeyBuilder public(String key,
{String? namespace, String sharedBy = ''})AtKey myPublicID = AtKey.public('phone', namespace: 'wavi', sharedBy: '@alice').build();static SelfKeyBuilder self(String key,
{String? namespace, String sharedBy = ''})AtKey mySelfID = AtKey.self('phone', namespace: 'wavi', sharedBy: '@alice').build();static SharedKeyBuilder shared(String key,
{String? namespace, String sharedBy = ''})AtKey sharedKey = (AtKey.shared('phone', 'wavi')
..sharedWith('@bob')).build();AtKey atKey = (AtKey.shared('phone', namespace: 'wavi', sharedBy: '@alice')
..sharedWith('@bob')
..cache(1000, true))
.build();#include <atclient/atkey.h>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 = "wavi";
if(atclient_atkey_create_public_key(
&my_public_atkey,
atkey_key,
atkey_shared_by,
atkey_namespace) != 0) {
// an error occurred
}atclient_atkey_free(&my_public_atkey);#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;
}
}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 = "wavi";
if(atclient_atkey_create_self_key(
&my_self_atkey,
atkey_key,
atkey_shared_by,
atkey_namespace) != 0) {
// an error occurred
}atclient_atkey_free(&my_public_atkey);#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;
}
}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(atclient_atkey_create_shared_key(&my_shared_atkey,
atkey_key,
atkey_shared_by,
atkey_shared_with,
atkey_namespace) != 0) {
// an error occurred
}atclient_atkey_free(&my_public_atkey);#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;
}
}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
}atclient_atkey_metadata *metadata = &(my_shared_atkey.metadata);if (atclient_atkey_metadata_set_ttl(metadata, 1 * 1000) != 0)
{
// an error occurred
}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);
}#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;
}
}How to send and receive real-time messages
In Dart, the AtClient is stored within the AtClientManager. Once an atSign has been onboarded, you will be able to access the AtClientManager for its associated atSign.
AtClientManager is a singleton model. When AtClientManager.getInstance() is called, it will get the AtClientManager instance for the last onboarded atSign.
AtClientManager atClientManager = AtClientManager.getInstance();As previously mentioned, the AtClientManager stores the actual AtClient itself. You can retrieve the AtClient by calling atClientManager.atClient.
You can subscribe to a stream of notifications like this:
You can send (a.k.a. publish) a notification like this:
Events is how we send and receive real-time messages in the atProtocol. The client SDK assists in using the atProtocol 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
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.
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.
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.
Calling atclient_monitor_read will attempt to read the network buffer for any notifications.
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!
See the Example Application on how to read the different kinds of values from the atclient_monitor_response struct.
Don't forget to free everything at the end of your application's life time
Also free the atclient_monitor_response struct if you haven't already.
You can find the full code of this example on our GitHub.
Since we are notifying another atSign, we have to set up a Shared atKey.
This process should be familiar to you if you have already gone through CRUD Operations.
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.
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.
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.
Don't forget to free everything
You can find the full code of this example on our GitHub.
AtClient atClient = atClientManager.atClient;AtClient atClient = AtClientManager.getInstance().atClient;
atClient.notificationService
.subscribe(regex: 'message.$nameSpace@', shouldDecrypt: true)
.listen((notification) {
print("Got a message: ${notification.value}");
});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),
);#include <atclient/monitor.h>atclient monitor_client;
atclient_init(&monitor_client);if (atclient_monitor_pkam_authenticate(
&monitor_client,
atserver_host,
atserver_port,
&atkeys,
"@jeremy_0") != 0) {
// an error occurred
}if(atclient_monitor_start(&monitor_client, ".*") != 0) {
// an error occurred
}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);atclient_free(&monitor_client);atclient_monitor_response_free(&response);#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;
}
}
#include <atclient/notify.h>atclient_atkey atkey;
atclient_atkey_init(&atkey);
if(atclient_atkey_create_shared_key(&atkey, "phone", "@jeremy_0", "@soccer99", "c_demos") != 0) {
// an error occurred
}atclient_notify_params notify_params;
atclient_notify_params_init(¬ify_params);
if(atclient_notify_params_set_atkey(¬ify_params, &atkey) != 0) {
// an error occurred
}
const char *value = "Hello! This is a notification!";
if(atclient_notify_params_set_value(¬ify_params, value) != 0) {
// an error occurred
}if(atclient_notify(&atclient1, ¬ify_params, NULL) != 0) {
// an error occurred
}atclient_atkey_free(&atkey);
atclient_notify_params_free(¬ify_params);#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(¬ify_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(¬ify_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(¬ify_params, value)) != 0) {
goto exit;
}
/*
* 2. Send the notification
* We will pass `NULL` for the commit id.
*/
if((exit_code = atclient_notify(&atclient1, ¬ify_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(¬ify_params);
return exit_code;
}
}How to do basic CRUD operations on an atServer
In Dart, the AtClient is stored within the AtClientManager. Once an atSign has been onboarded, you will be able to access the AtClientManager for its associated atSign.
AtClientManager is a singleton model. When AtClientManager.getInstance() is called, it will get the AtClientManager instance for the last onboarded atSign.
AtClientManager atClientManager = AtClientManager.getInstance();As previously mentioned, the AtClientManager stores the actual AtClient itself. You can retrieve the AtClient by calling atClientManager.atClient.
Before you can do anything with an atRecord, you need an to represent it.
If you don't know how to create an atKey, please see the first.
To create data the put method is used, this method accepts text (String) or binary (List<int>).
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.
The bytes [1, 2, 3, 4] have now replaced the string "123-456-7890".
There are two parts to reading data using the atClient SDK:
Scanning for and listing out the atKeys for atRecords that can be retrieved
Retrieving the atRecord for a given atKey
1. Scanning and listing atKeys
There are two methods available for scanning and listing atKeys:
getAtKeys which provides the list in Class format (i.e. List<AtKey>)
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:
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:
All atKeys which end with ".wavi" in the record identifier part:
2. Retrieving atRecords by atKey
To retrieve an atRecord, you must know the atKey and pass it to the get method.
Calling this function will return an AtValue, and update the atKey passed to it with any changes to the metadata.
Usage
To delete data, simply call the delete method with the atKey for the atRecord to delete.
Usage
See to learn about synchronization, which supports syncing of a local atServer, allowing CRUD operations to work even if the application has no internet access.
You can find the API reference for the entire package available on .
The AtClient class API reference is available .
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.
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.
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.
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.
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.
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.
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.
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.
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.
Once we are done with the value itself, we should free it to avoid any memory leaks.
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.
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.
Once we are done with the value itself, we should free it to avoid any memory leaks.
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.
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.
Once we are done with the value itself, we should free it to avoid any memory leaks.
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.
atclient_delete will delete the atKey from your atServer.
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.
AtClient atClient = atClientManager.atClient;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);Future<bool> put(
AtKey key,
dynamic value,
{bool isDedicated = false,
PutRequestOptions? putRequestOptions});List<int> binaryData = [1, 2, 3, 4];
bool res = await atClient.put(myID, binaryData);Future<List<AtKey>> getAtKeys(
{String? regex,
String? sharedBy,
String? sharedWith,
bool showHiddenKeys = false});List<AtKey> allIDs = await atClient.getAtKeys();List<AtKey> waviIDs = await atClient.getAtKeys(regex: '^.*\.wavi@.+$');Future<AtValue> get(
AtKey key,
{bool isDedicated = false,
GetRequestOptions? getRequestOptions});AtValue atValue = await atClient.get(myID);
String? text = atValue.value;Future<bool> delete(
AtKey key,
{bool isDedicated = false});bool res = await atClient.delete(myID);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
}const char *atkey_value = "123-456-7890";
if (atclient_put_public_key(&atclient, &my_public_atkey, atkey_value, NULL, NULL) != 0)
{
// an error occurred
}#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;
}
}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
}const char *atkey_value = "123-456-7890";
if (atclient_put_self_key(&atclient, &my_self_atkey, atkey_value, NULL, NULL) != 0)
{
// an error occurred
}#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;
}
}
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
}const char *atkey_value = "123-456-7890";
if (atclient_put_shared_key(&atclient, &my_shared_atkey, atkey_value, NULL, NULL) != 0)
{
// an error occurred
}#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;
}
}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
}char *value = NULL;
if (atclient_get_public_key(&atclient, &my_public_atkey, &value, NULL) != 0)
{
// an error occurred
}free(value);#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;
}
}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
}char *value = NULL;
if (atclient_get_self_key(&atclient, &my_self_atkey, &value, NULL) != 0)
{
// an error occurred
}free(value);#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;
}
}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
}char *value = NULL;
if (atclient_get_shared_key(&atclient, &my_self_atkey, &value, NULL) != 0)
{
// an error occurred
}free(value)#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;
}
}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
}if (atclient_delete(&atclient, &my_shared_atkey, NULL, NULL) != 0) {
// an error occurred
}#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;
}
}