Aranya Documentation An overview of the Aranya project

Aranya Beta

Introduction

A product specification of the standalone Aranya daemon. The goal is to provide a commercial off the shelf solution for integrating Aranya. Customers should be able to download the Aranya daemon, setup a team, and begin using Aranya.

Primary Goals:

  1. Provide a low friction solution customers can use to better secure their infrastructure.
  2. Easily setup a team and onboard users.
  3. Implement a default policy that works well enough for a wide variety of situations that customers can relate to.
  4. Provide stable APIs that allow devices and users to interact with Aranya.
  5. Expose an API for point to point high performance encrypted communication using IPv4 for transport.

Secondary internal goals:

  1. Design for the ability to swap out policies in a future version.
  2. Design for future improvements, like additional data planes and configurable roles.

Design

Aranya is a decentralized message delivery platform with authorization built in. Below are some basic requirements for running the beta version of Aranya:

System Requirements:

  • We will target x86, ARM, and ARM64 running Linux.
  • We will assume there is IPv4 connectivity.

After the beta is working, the following values should be measured to estimate system requirements:

  • Memory usage
  • Disk requirements:
    • Storage amount
    • Storage device write speed
    • Storage device seek speed

Architecture

There will be two subsystems as part of Aranya:

  1. The daemon
  2. The client library

The daemon is a standalone process that runs Aranya and exposes the control plane API via IPC. The daemon will periodically sync with peers and handle commands as they are synced. The current design does not require any disk-persisted effects, so on startup the daemon should start from a clean state (aside from loading accounts) every time it is started. This includes clearing any shared memory on startup to avoid collisions/stale data.

The client library exists inside the user’s process and is used to interact with the daemon. The client library also includes a light wrapper around AFC allowing the user to encrypt data and send messages to peers.

Both subsystems are limited to a single KeyBundle per process to limit the possibility of leaking device keys. This still enables the daemon to participate in multiple teams by using the same keybundle for every team.

daemon subsystems

daemon subsystems with additional detail

For V1, we will initially design for the C API. The Rust client library is a bonus that we gain from the Rust -> C compilation. The C API should include autogenerated docs.

Config

On startup, the daemon requires the work directory as well as the path to the UDS socket that should be created. All other config values are provided when a client context is initialized and passed to the daemon over the UDS socket. This approach allows the user API to drive the configuration of the daemon, and can help reduce errors in config mismatch by minimizing the number of duplicate config values.

Components

The system is made up of multiple components:

The “client API” is the top level component and contains local operations such as enabling/disabling syncing or initializing a new device.

The Access Control Plane is the top level control plane, enabling IDAM operations and other on-graph operations. The Access Control Plane is used to manage keys, address assignment, roles, and labels as set out in the policy.

For the beta, the AFC Plane will be build to provide a simple API for sending and receiving messages using AFC. The AFC Plane contains its own control plane for control messages, as well as the main data plane for moving data between devices.

Component structure:

  • Local Client API: syncing, local device management
    • Access control plane (IDAM control plane): IDAM lifecycle
      • AFC plane (Beta)
        • AFC control plane
        • AFC data plane
      • Broker plane (later…)
        • Broker control plane
        • Broker data plane
      • On graph messaging (later…)
        • graph messaging control plane (or implicit)
        • data plane (on graph messages)
      • … additional planes in future versions

Client APIs

The client APIs are local only API endpoints that do not create commands on the graph. They are mostly used to manage the local state.

  • InitDevice() -> device_id - init a device if not exists, generate keys, create the API instance, etc.
  • GetKeyBundle() -> keybundle - returns the current device’s public key bundle.
  • GetDeviceId() -> device_id - returns the device’s device ID.
  • AddTeam(team_id) -> bool - add an existing team to the local device store. Not an Aranya action/command.
  • RemoveTeam(team_id) -> bool - remove a team from the local device store. Not an Aranya action/command.
  • SerializeKeyBundle(scheme, keybundle) -> bytes - serialize a keybundle to a given format/scheme. Ideally, this can be used to serialize to either human readable or machine readable formats.
  • DeserializeKeyBundle(scheme, bytes) -> keybundle - desrialize a keybundle from a given format/scheme.
  • SerializeId(scheme, id) -> bytes - serialize an ID to a given format/scheme.
  • DeserializeId(scheme, bytes) -> id - deserialize an ID from a given format/scheme.

Sync API:

No policy command associated with these commands.

  • AddSyncPeer(address, team_id, rate) -> bool - add a peer to start syncing with at a specific rate. Syncing should support DNS resolution in the case a domain name is used.
  • RemoveSyncPeer(address, team_id) -> bool - remove a sync peer associated with the given address, team_id.

Onboarding API

Beta:

Easy to implement, key moving is done by integration.

  1. Create Device (NewDevice)
  2. Get Device Key (Current device KeyBundle (what you give to the admin))
  3. Give KeyBundle to admin on team (integration problem)
  4. Admin does AddDevice(user_key_bundle)
  5. Get team_id from admin (integration detail)
  6. Add team_id to client (AddTeam)
  7. Sync with device on team (AddSyncPeer)

MVP:

We will provide an additional optional onboarding API:

TODO: we need a flow chart or just event sequence for these.

  • CreateInvite(server address, team_id) -> Invite code - create an invite code that will be sent to a device to onboard.
  • PollInvite(server address, Invite Code) -> Option - check the status of an invite code, returns the device ID of the device that joined using that code.
  • Join(server address, Invite Code, Device Key) -> Result - join a team using an invite code, returns the team_id of the team that was joined.

The goal of this onboarding API is to simplify the process of onboarding a user/device by providing an invite code instead of passing a KeyBundle.

IDAM Control Plane API

The IDAM control plane is for managing identity and authorization by interacting with the graph. Each endpoint creates one or more commands on the graph.

  • CreateTeam(owner) -> team_id - initialize the graph, creating the team with the author as the owner.
  • CloseTeam(team_id) - close the team and stop all operations on the graph.
  • AddDevice(team_id, keybundle, common_name) -> device_id - add a device to the team with the default role
  • RemoveDevice(team_id, device_id) - remove a device from the team
  • AssignRole(team_id, device_id, role) -> bool - assign a role to a device
  • RevokeRole(team_id, device_id, role) -> bool - remove a role from a device
  • AssignNetworkName(team_id, device_id, net_identifier) -> bool - associate a network address to a device for use with AFC. If the address already exists for this device, it is replaced with the new address. Capable of resolving addresses via DNS, required to be statically mapped to IPv4. For use with CreateChannel and receiving messages. Can take either DNS name or IPv4. MVP would need reverse lookup. More work required on the address assignment, especially post-beta. Currently one name per device.
  • RevokeNetworkName(team_id, device_id, net_identifier) -> bool - disassociate a network address from a device.
  • CreateLabel(team_id, label) -> bool - create a label
  • DeleteLabel(team_id, label) -> bool - delete a label
  • AssignLabel(team_id, device_id, label) -> bool - assign a label to a device so that it can be used for AFC
  • RevokeLabel(team_id, device_id, label) -> bool - revoke a label from a device

The IDAM control plane will be managed by the daemon process which will be accessed with the previous APIs via an IPC mechanism. The daemon will be responsible for syncing state with peers. To enable, these two additional APIs will be provided to add and remove peers to sync from:

Initially, UDS and shm will be used for IPC between the daemon and client library.

Role APIs need improvement post beta (to support custom roles?).

AFC Plane API

The AFC plane is split in two different sub-planes: the AFC control plane and the AFC data plane. The AFC control plane is responsible for any Aranya command or ephemeral commands, while the AFC data plane contains only data related APIs.

AFC Control Plane

  • CreateChannel(team_id, net_identifier, label) -> channel_id - Open a channel to the dest with the given label. The user API transparently handles sending the ephemeral command to the peer. Channel keys are automatically rotated after a specific byte count.
  • DeleteChannel(team_id, channel_id) -> bool - Close a channel with the given ID. The user API transparently handles sending the ephemeral command to the peer.

AFC Data Plane

  • PollData(timeout) -> bool - blocks with timeout, returns true if there is AFC data to read using ReadData.
  • ReadData(bytes_buffer) -> Result<(len_written, remote_net_identifier, label, channel_id)> - read the AFC data, returning the plaintext, sender, and label (or an error). Returns a single AFC message at a time.
  • SendData(bytes, channel_id, timeout) -> bool - encrypts and sends the data to the given channel with a timeout. This call is blocking until the timeout is complete.

For the initial implementation in the beta, AFC control messages should be handled transparently by the client library. In the future, control messages can be passed to the user to be manually forwarded to the daemon using a different API.

Roles & Permissions

There will be 4 different user roles with the following set of permissions for each.

owner

  • root user (all permissions excluding sending data on AFC label)
  • create/close team
  • add/remove devices
  • elevate/revoke permissions for devices up to and including owner
  • define/undefine AFC labels
  • assign/remove addresses/names for AFC
  • assign/revoke AFC labels
  • sync

admin

  • elevate/revoke permissions for devices up to a max level of operator
  • define/undefine AFC labels
  • assign/remove addresses/names for AFC
  • cannot send data on AFC labels
  • sync

operator

  • add (new) / rm user in team
  • assign user role to users in team
  • assign/revoke AFC labels
  • assign/remove addresses/names for AFC
  • cannot send data on AFC labels
  • sync

user

  • use AFC
  • sync

sync-role

  • sync

Notes:

  • Devices can always remove and demote themselves
  • Devices of equal role cannot remove or demote each other (hence, capabilities of an owner can only be self-reduced)

Documentation

API documentation must be provided for the client API covering the functions and behavior of each API call. Most likely, this will take the form of a doxygen-like web page. Developers can use this to look up language agnostic functions for operating the client API. The documentation should also include tutorials and a quickstart to get developers up and running with Aranya as soon as possible. Documentation should also be provided for the daemon so that developers and sysadmins can understand the requirements and operations of the daemon.

Glossary

  • AFC - the library used to do high performance encryption using keys managed by Aranya.
  • Aranya - the main library that drives the control plane and policy execution.
  • daemon - a long-lived process, typically running in the background, that handles commands and keeps state.
  • device - a computer, sometimes associated with a user but can also be independent. In this model, we consider devices instead of users directly to accommodate autonomous entities.
  • IPC - inter-process communication.
  • policy - an Aranya policy, containing the logic and rules of the system.
  • sync - a request to synchronize the commands on the control plane. Syncs are currently pull only, so the device that requests a sync receives commands from the requestee.
  • team - a group of devices with an associated policy.
  • UDS - Unix Domain Sockets, used for communication between processes.
  • user - a person who may operate a device.

Verb pairs

  • Add/Remove
  • Assign/Revoke
  • Create/Delete

Additional Notes

  • All APIs use Result unless stated otherwise
  • Need something similar to TLS SNI (ident name you are talking to inside TLS connection, plus reverse SNI aka name you are talking from) for AFC messages.
  • Association of addresses and names is a security property, otherwise devices can impersonate
  • Eventually want multiple channels on same label instead of current AFC terminology which associates a channel as a (note_id, label) tuple.
  • Possibly use TCP over QUIC
  • Future Client APIs might include endpoints for Fact DB queries.

  • Beta - end of Q3
  • MVP - end of year? more polished version, stable API