Flutter
Learn how the Flutter SDK works
Steps in this module
Key Basics
Overview
To store data, atPlatform utilizes a straightforward key-value method. A key serves as a unique identifier in key-value pairs.
AtKey Structure:
Attributes | Description | Return Type |
---|---|---|
key | Unique Identifier for the data | String |
sharedWith | The atSign to share with | String |
sharedBy | The atSign that is sharing | String |
namespace | The namespace of the app | String |
metadata | Metadata | |
isRef | Bool |
Metadata
Metadata In AtKey allows you to add more aspects of AtKey. If you want your data to live for a given period of time, or if you want it to be born/deleted after a set amount of time, metaData can help you with that.
Metadata Structure:
Attributes | Capabilities | Return Type |
---|---|---|
ttl | int | |
ttb | int | |
ttr | int | |
ccd | bool | |
availableAt | DateTime | |
expiresAt | DateTime | |
refreshAt | DateTime | |
createdAt | DateTime | |
updatedAt | DateTime | |
dataSignature | String | |
sharedKeyStatus | bool | |
isPublic | bool | |
isHidden | bool | |
namespaceAware | bool | |
isBinary | bool | |
isEncrypted | bool | |
isCached | bool |
Key Creation rules
A key in atProtocol has the structure described below.
<Key Visibility>|<sharedWith>:<entity>.<namespace>@<sharedBy/owner>
A key in atProtocol should adhere to following rules:
Length of a key should not be more than 240 characters (Limited by the current impl of the secondary. Not a protocol limitation)
Max of fifty five 7-bit characters for the atSign
Allowed characters in an entity are: [\w._,-’”
] Namespace is mandatory in the current implementation of the protocol
If cached then the owner of the key should not be same as current atSign
sharedWith and sharedBy/owner cannot be same for a shared key
sharedWith and sharedBy/owner should be same for a self key (Private, Hidden (_), Just self)
Reserved keys in protocol, private keys and hidden keys cannot be notified Ex: notify:_secret@jagan:sitaram
Protocol prevents creation of a key with a owner/sharedBy using an atSign that’s not owned by the current user.
Key cannot be a reserved key.
List of reserved keys:
- 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
atPlatform CRUD operations
Create/Update Data atPlatform SDK exposes two methods to create the data. They are:
- put
- putMeta
put
Update’s value of key is if it is already present. Otherwise creates a new key.
To share a key to another atSign, use AtKey.shared() factory method or populate AtKey.sharedWith with the atSign to whom the data has to be shared. Further, notifies the sharedWith atSign that a key has been created.
Signature
Future<bool> put(AtKey key, dynamic value);
Accepts an instance of AtKey and value and stores it in the local storage(local secondary) has a key-value pair.
AtKey represents the key against which the value will be stored. It further describes to whom the data is shared and metadata of the key. key rules
The value can be either a textual information or a binary data (e.g. Images, Files etc ). Returns a boolean value that represents the status of the put method. Returns ‘TRUE’ when put is completed successfully, else returns false.
Throws AtClientException with an error code and error description that describes that cause of the exception.
putMeta
Updates the metadata of the key.
Signature
Future<bool> putMeta(AtKey key);
Accepts an Instance of AtKey. The Metadata is encapsulated in the AtKey. Set the new/updated metadata to [Atkey.Metadata].
Returns a boolean value that represents the status of the putMeta method. Returns ‘TRUE’ when putMeta is completed successfully, else returns false.
Throws AtClientException with an error code and error description that describes that cause of the exception.
Read Data
get
Get the value of the key from the user’s cloud secondary.
Signature
Future<AtValue> get (AtKey key);
Accepts an Instance of AtKey. The Metadata is encapsulated in the AtKey. Fetch both the value and metadata of the key.
Returns a boolean value that represents the status of the ‘get’ method. Returns ‘TRUE’ when ‘get’ is completed successfully, else returns false.
Throws AtClientException with an error code and error description that describes that cause of the exception.
getMeta
Gets the metadata of the key.
Signature
Future <bool> getMeta(AtKey key);
Accepts an Instance of AtKey. The Metadata is encapsulated in the AtKey. Gets the metadata of the key.
Returns a boolean value that represents the status of the putMeta method. Returns ‘TRUE’ when getMeta is completed successfully, else returns false.
Throws AtClientException with an error code and error description that describes that cause of the exception.
Delete Data
Deletes the key.
Signature
Future<bool> delete(AtKey key);
Accepts an Instance of AtKey. Deletes the key from the storage. If the key is shared with another atSign, notifies the other atSign on the deletion of the key.
Returns a boolean value that represents the status of the delete method. Returns ‘TRUE’ when delete is completed successfully, else returns false.
Throws AtClientException with an error code and error description that describes that cause of the exception.
List of Keys
getKeys
Get all the keys stored in the user’s secondary in string format.
Signature
Future <<List<String>> getKeys({String? regex, String? sharedBy, String? sharedWith});
If regex is specified only matching keys are returned, giving you the flexibility to use different regexes for different types of data.
getAtKeys
Get all the keys stored in the user’s secondary in [AtKey] format.
Signature
Future <<List<AtKey>> getAtKeys({String? regex, String? sharedBy, String? sharedWith});
If regex is specified only matching keys are returned, giving you the flexibility to use different regexes for different types of data.
Notification
atProtocol data visibility
The atProtocol saves data created in a key-value pair format. atProtocol lets you create data with three levels of visibility. They are public, shared and self data. In the atProtocol a piece of data is identified as a certain data type based on the key structure. In the SDK, AtKey exposes abstractions to create keys for the various data types. Below sections describe this in detail.
Public data
Public data can be looked up without authentication. I.e. if @bob wants to lookup public data in the secondary server of @alice, @bob does not need to prove to @alice that he is @bob to see the data publicly shared by @alice. Public view of the @alice’s data will be the same for any user on the atPlatform. I.e. if @jane and @john look up some public data from @alice, it will be exactly the same as what @bob sees.
Good examples of public data in the real world are “first name” and the “last name”. We often don’t mind sharing our name with people, even random strangers.
Signing of public data Public data is signed with the “private keys” of the user. Which means when @alice looks up @bob’s public data, @alice knows for sure that the data is indeed from @bob.
Creating a public key
var publicKey = AtKey.public('phone', namespace: 'wavi')
.build();
Persisting public data
final atClientManager = await AtClientManager.getInstance()
.setCurrentAtSign(‘@alice, 'me', AtClientPreference());
var atClient = atClientManager.atClient;
var phoneKey = AtKey.public('phone', namespace: 'wavi')
.build();;
var value = '+1 100 200 300';
var putResult = await atClient.put(phoneKey, value);
Shared data
Shared data does exactly what it says it does! It is created in order to share it with other people. These are the people we typically know and trust. When @bob shares his phone number with @alice, then only @alice can see that piece of data. Good examples of shared data in the real world are “phone number” and “email”. We only share these details with the people we know and trust.
Encryption of shared data
When @bob shares data to @alice, @bob generates a shared key for @alice and encrypts the data with the shared key. Further, the shared key is encrypted with the @alice public key. So the data shared is cryptographically secure.
Creating a shared key
var sharedKey = AtKey.shared('phone', namespace: 'wavi')
..sharedWith('@bob').build();
Persisting shared data
final atClientManager = await AtClientManager.getInstance()
.setCurrentAtSign(‘@alice, 'me', AtClientPreference());
var atClient = atClientManager.atClient;
var phoneKey = AtKey.shared('phone', namespace: 'wavi')
..sharedWith('@bob')
.build();
var value = '+1 100 200 300';
var putResult = await atClient.put(phoneKey, value);
Self data Self data is created for self. If @bob creates data for self, only @bob and no one else can see the data.
Good examples of self data are “passwords” and “financial data”. We keep these details to ourselves.
Encryption of self data
When @bob creates data for self it is encrypted using @bob’s self encryption key.
Creating a self key
var selfKey = AtKey.self('phone', namespace: 'wavi')
.build();
Persisting self data
final atClientManager = await AtClientManager.getInstance()
.setCurrentAtSign(‘@alice, 'me', AtClientPreference());
var atClient = atClientManager.atClient;
var phoneKey = AtKey.self('phone', namespace: 'wavi')
.build();
var value = '+1 100 200 300';
var putResult = await atClient.put(phoneKey, value);
All of the above rules are implemented and exposed using the validate() method on AtKey. These rules are enforced during creation of the data.
Key attributes of atProtocol data
TTL - Time to live
TTL (Time To Live) is the duration of time for which the key will be available. TTL is expressed in milliseconds. Adding TTL during the key creation results in the time at which the key expires.
// Below key expires in 1 minute
var phoneKey = (AtKey.shared('phone', namespace: 'wavi')
..sharedWith('bob')
..timeToLive(60000))
.build();
var value = '+1 100 200 300';
var putResult = await atClient.put(phoneKey, value);
Scan/List behavior with TTL A key whose ttl has been set will be listed until a background job that runs at frequent time intervals deletes all the expired keys
Lookup/Get behavior with TTL As long as the key has not expired, a “lookup” of the key returns the value that was set originally. After the key expiry, ‘null’ is returned when the key is looked up for a value.
TTB - Time to birth
TTB (Time To Birth) is the span of time within which the key’s value is not available. TTB is expressed in milliseconds. Adding TTB during the key creation results in the time at which the key’s value is available.
// Below key is available after 1 minute
var phoneKey = (AtKey.shared('phone', namespace: 'wavi')
..sharedWith('bob')
..timeToBirth(60000))
.build();
var value = '+1 100 200 300';
var putResult = await atClient.put(phoneKey, value);
Scan/List behavior with TTB
A key whose ttb has been set will be listed.
Lookup/Get behavior with TTB
Until the key’s TTB is met, a “lookup” of the key returns ‘null’. After which the original value of the key is returned.
TTR - Time to refresh
TTR (Time To Refresh) is the span of time that represents the time interval at which the cached key will be refreshed with a new value(if the original key’s value is updated). TTR is expressed in seconds. Further, TTR can also be set to ‘-1’ which represents the value of the original key will not be updated and therefore the cached key need not be refreshed.
Cacheability
All of the data created resides in the secondary server of an atSign. The owner of an atSign can choose to allow the public and the shared data to be cached in the another atSign
// Below key refreshes everyday
// TTR accepts time units in seconds
var phoneKey = (AtKey.shared('phone', namespace: 'wavi')
..sharedWith('bob')
..cache(86400, true))
.build();
var value = '+1 100 200 300';
var putResult = await atClient.put(phoneKey, value);
Scan/List behavior with TTR
All the keys with TTR and the cached keys that are created as a result of TTR are listed.
Lookup/Get behavior with TTR
During lookup of a key, fetches for availability of the corresponding cached key. If a cached key is available, the value is returned. If cached is not available, fetches the value from the original key.
Update behavior with TTR Only the owner of the key reserves the right to update the value. Updating the cached key’s value is prohibited.
Propagation of an update to the cached key
When an original key is updated by the creator, the ones who have cached it need to update the cached copy too. This can happen in two ways:
- The Auto-Notify notifies the updated value to another atSign, thereby the cached key’s value is updated.
- The refresh job in the secondary server
CCD - Cascade delete
CCD (Cascade delete) compliments the TTR functionality by allowing the owner of the key to decide if the cached key has to be deleted upon the deletion of the original key. CCD accepts a boolean value(either true (or) false). When set to true, upon deletion of the original key, the cached key is also deleted. Setting it to false results in the cached key remaining, even after the original key is deleted.