Cwtch Stable API

This commit is contained in:
Sarah Jamie Lewis 2023-01-09 11:59:10 -08:00
parent a8f93684e8
commit ae24753b14
1 changed files with 227 additions and 0 deletions

View File

@ -0,0 +1,227 @@
---
title: Cwtch Stable API Design
description: "The post outlines the general principles that are guiding the development of Cwtch Stable, the obstacles that prevent a stable Cwtch release, and closes with an overview the next steps and a timeline to tackle them."
slug: cwtch-stable-api-design
tags: [cwtch, cwtch-stable, planning, api]
image: /img/devlog1_small.jpg
hide_table_of_contents: false
toc_max_heading_level: 4
authors:
- name: Sarah Jamie Lewis
title: Executive Director, Open Privacy Research Society
image_url: /img/sarah.jpg
---
Cwtch grew out of a prototype and has been allowed to evolve over time as we discovered better ways of implementing safe and secure metadata resistant communications.
As we have grown we have placed experimental functionality where it was most accessible to place, not necessarily where it was best to place it, this has led to some degree of overlapping and inconsistent responsibilities across Cwtch software packages.
As we move out of Beta and towards Cwtch Stable it is time to revisit these previous decisions with the benefit of hindsight and years of real-world testing.
In this post we will outline planned new changes to the Cwtch API that realign responsibilities, and explicitly enable new functionality to be built in a modular, controlled, and secure way. Ready for Cwtch Stable, and beyond.
<!--truncate-->
### Clarifying Terminology
Over the years we have evolved how we talk about the various parts of the Cwtch ecosystem. To make this document clear we have revised and clarified some terms.
- **Cwtch** refers to the overall ecosystem including all the component libraries, bindings, and the flagship Cwtch application.
- **Cwtchlib** refers to the reference implementation of the Cwtch Protocol / Application framework, currently written in Go.
- **Bindings** refers to C/Java/Kotlin/Rust bindings (primarily libcwtch-go) that act as an interface between Cwtchlib and downstream applications.
- `CwtchPeer` is where the reference Cwtch API is defined. It is responsible for managing the state of a single Cwtch Profile, persistence (e.g. storing messages), and automatically reacting to certain messages like message acknowledgements and providing public profile attributes (e.g. profile display name).
- `ProtocolEngine` is responsible for maintaining networking resources like listening threads, peer connections, ephemeral server connections. At present, `ProtocolEngine` is also responsible for automatically responding to certain kinds of messages like providing file chunks for shared files.
### Tenets of the Cwtch API Design
Based on the tenets we have laid out for the Path to Cwtch Stable, we have adopted the following guiding principles for a new API design:
- **Flexibility** - new features and functionality can be implemented in Cwtch without adding new functions or dependencies to existing Cwtch interfaces.
- **Completeness** - all behaviour is either defined in the official library, or explicitly deferred to applications, no special behaviour is implemented by intermediate wrappers.
- **Security** experiments should not compromise existing Cwtch functionality - and should be able to be turned on/off at any time without issue.
### The Cwtch Experiment Landscape
A summary of the experiments that are currently implements or in design, and the changes to the code that were required to support them.
- **Groups** te very first prototypes of Cwtch were designed around group messaging and, as such, multi-party chats are the most integrated experiment within Cwtch sharing interfaces with P2P chat and requiring specialized Protocol Engine functionality to manage ephemeral connections and antispam tokens, including the introduction of new peer events like NewMessageFromGroup.
- **Hybrid Groups** - we have plans to upgrade the Groups experience to a more flexible “hybrid-groups” protocol which requires additional custom hook-response that needs to be tightly controlled and isolated from other parts of the system.
- **Filesharing** like Groups, Filesharing is a cross-cutting feature that required new APIs, Hooks into Peer Events, and additional capability in the ProtocolEngine itself.
- **Profile Images** based on Filesharing and the core get/val functionality, there are only a few small parts of the codebase that are explicitly dedicated to profile images, and these are all event-based reactions that currently reside in the event-decoration module of licwtch-go, but could easily be moved to a standalone module if a hook-based API was available.
- **Server Hosting** the only example of an Application-level experiment in Cwch at present. This functionality requires no changes to the cwtchlib module, but is mainly implementing in the libcwtch-go bindings themselves. Ideally this functionality would be moved into a standalone package.
- **Message Formatting** notable as the the main example of a former experimental-functionality that was promoted to an optional feature, but because it is entirely UI based in implementation there are few insights that can be gained from its history
- **Search / Microblogging** proposed features that would require database access/changes in order to implement fully and efficiently, any proposed changes to the Cwtch API should allow for the possibility of new functionality at all layers of the Cwtch stack, including storage.
- **Status / Profile Metadata** proposed features that only require specific APIs / hooks for saving requested information for the purposes of caching.
### The Problem with Experiments
We have done some work in past to limit the impact an experimental feature can have on the rest of Cwtch, mainly through providing restricted sets of public Cwtch APIs e.g. the `SendMessages` interface that only allows callers to send messages.
We have also worked to package experimental functionality into so-called **Gated Functionalities** that are only available if a given experiment is turned on.
Together, these form the current basis for implementing to Cwtch features in the official libraries, but they are not without problems:
- The scope of a functionality is rather broad, and can only be passed a complete Cwtch profile or a denoted subset of functionality e.g. `SendMessages` there is no current way to scope a function to a given conversation, or to a given set of attributes.
- The implementation of experiments has mostly been delegated to libCwtch-go and, as such, the gating inside CwtchLib is limited, often relying on state to be passed into it by libcwtch-go, or relying on libcwtch-go to explicitly disable the functionality.
- This lack of ownership over experiments by the official CwtchLib means that libraries based on Cwtch instead of libcwtch-go do not have access to the safeguards provided by libcwtch-go.
### Restricting Powerful Cwtch APIs
To carefully expand Cwtch out using additional experimental APIs we must work to limit the impact further e.g. restricting actions to a given type of conversation, or only executing actions at registered times. To do this we require three separate but related strands of work:
- Assume responsibility for experiments and features in Cwtch itself so that Cwtchlib has direct access to which experiments are enabled at any given time. Doing this allows changes to settings to always flow through Application and, (as currently happens with Anonymous Communication Network (ACN) state), provides a natural point at which to interface those changes into a Cwtch Profile.
- Finer-grained Interfaces that allow restricting actions to preregistered conversation types e.g. a `RestrictedCwtchConversationInterface` which decorates a Cwtch Profile interface such that it can only interact with a single conversation these can then be passed into hooks and interface functions to limit their impact.
- Registered Hooks at pre-specified points with restricted capabilities to allow experimental functionality to register interest in certain events, and act on them at the correct time, and to allow `CwtchPeer` to control which experiments get access to which events at a given time.
#### Pre-Registered Hooks
In order to implement certain functionality actions need to take place in-between events handled by `CwtchPeer`. As a motivating example consider a new group membership protocol overlayed above the existing messages. Such a protocol may require checking against group permission settings after receiving a new message, but before inserting it into into the database (e.g. the message author needs to be confirmed against the list of current members authorized to post to the group).
This is currently only possible with invasive changes to the `CwtchPeer` interface, explicitly inserting a hook point and acting on it. In an ideal design we would be able to register such hooks for most likely events without additional development effort.
We are introducing a new set of Cwtch APIs designed for this purpose:
- `OnNewPeerMessage` - hooked prior to inserting the message into the database.
- `OnPeerMessageConfirmed` hooked after a peer message has been inserted into the database.
- `OnEncryptedGroupMessage` hooked after receiving an encrypted message from a group server.
- `OnGroupMessageReceived` hooked after a successful decryption of a group message, but before inserting it into the database.
- `OnContactRequestValue` hooked on request of a scoped,zoned, and keyed value from a peer.
- `OnContactReceiveValue` hooked on receipt of a requested scoped,zoned, and keyed value from a peer.
Including the following APIs for managing hooked functionality:
- `RegisterEvents` - returns a set of events that the extension is interested processing.
- `RegisterExperiments` - returns a set of experiments that the extension is interested in being notified about
- `OnEvent` - to be called by `CwtchPeer` whenever an event registered with `RegisterEvents` is called (assuming all experiments registered through `RegisterExperiments` is active)
#### Protocol Engine Subsystems
As mentioned in our experiment summary, some functionality needs to be implemented directly in the Protocol Engine. The protocol engine is responsible for managing networking clients, and sending/receiving packets from those clients to/from a CwtchPeer (via the event bus).
Some types of data are too costly to send over the event bus e.g. requested chunks from shared files, and as such we need to delegate the handling of such data to a Protocol Engine.
At the moment is this done through the concept of informal “subsystems”, modular add-ons to Engine that process certain events. The current informal nature of this design means that there are not hard-and-fast rules regarding what functionality lives in a subsystem, and how subsystems interact with the wider protocol engine ecosystem.
We are formalizing this subsystem into an interface, similar to the hooked functionality in `CwtchPeer`:
- `RegisterEvents` - returns a set of events that the subsystem needs to consume to operate.
- `OnEvent` to be called by `ProtocolEngine` whenever an event registered with `RegisterEvents` is called (assuming all experiments registered through `RegisterExperiments` is active)
- `RegisterContexts` - returns the set of contexts that the subsystem implements e.g. `im.cwtch.filesharing`
This also requires a formalization of two *engine specific* events (for use on the event bus):
- `SendCwtchMessage` encapsulating the existing `CwtchPeerMessage` that is used internally in engine to encapsulate state.
- `CwtchMessageReceived` encapsulating the existing `handlePeerMessage` function which effectively already serves this purpose, but instead of using an Observer pattern, is implemented as an increasingly unwieldy set of if/else blocks.
And the introduction of three **additional** engine specific events:
- `StartEngineSubsystem` replaces subsystem specific start event, can be driven by functionalities to (re)start protocol specific handling.
- `StopEngineSubsystem` replaces subsystem specific stop event mechanisms, can be driven by functionalities to stop all protocol specific handling.
- `SubsystemStatus` a generic event that can be published by subsystems with a collection of fields useful for debugging
This will allow us to move the following functionality, currently part of Protocol Engine itself, into generic subsystems:
- **Attribute Lookup Handling** - this functionality is currently part of the overloaded `handlePeerMessage` function, filtered using the `Context` parameter of the `CwtchPeerMessage`. As such it can be entirely delegated to a subsystem.
- **Filesharing Chunk Request Handling** this is also part of handlePeerMessage, also filtered using the `Context` parameter, and is already almost entirely implementing in a standalone subsystem (only routing is handled by `handlePeerMessage`)
- **Filesharing Start File Share/Stop File Share** this is currently part of the `handleEvent` behaviour of `ProtocolEngine` and can be moved into an `OnEvent` handler of the file sharing subsystem (where such events are already processed).
The introduction of pre-registered hooks in combination with the formalizations of Protocol Engine subsystems will allow the follow functionality, currently implemented in `CwtchPeer` or libcwtch to be moved entirely to additional functionality:
- **Filesharing** makes heavy use of the getval/retval functionality, we can move all of this into a hooked-based functionality extension.
- Filesharing also depends on the file sharing subsystem to be enabled in Engine. This engine is responsible for processing chunk requests. See Registered Engine Subsystems.
- **Profile Images** we treat profile images as a specialization of the file sharing function, as such the experiment can operate entirely over apis provided by the filesharing experiment. (Right now this specialization lives in libcwtch as hooks into the relevant functions)
- **Legacy Groups** while groups themselves are a first-class consideration for Cwtch, the actual process of constructing and receiving group messages relies heavily on processing of events, or interpreting generic conversation attributes, and as such this functionality can be moved entirely to hooked-based functionality. By doing this we also open the path towards additional group constructions implemented over the same interface.
- **Status/Profile Metadata** status depends entirely on OnPeerRequestValue / OnPeerReceiveValue and requires little Cwtch Peer interaction other than saving the result.
#### Impact on Enabling (Powerful) New Functionality
None of the above restricts our ability to introduce new functionality in to Cwtch that is dependent on more invasive changes (e.g. direct database access / updates), but they do allow us to structure such changes into discrete modules:
- **Search** a fulltext search feature requires new indexes to be created in Cwtch Storage (likely using the sqlite FT5 module). As an experiment SearchFunctionality would need access to a hook after database setup in order to create and populate those indexes. This is a far more powerful feature than most as it requires direct database access.
- **Non Chat Conversation Contexts** - the storage backend work we implemented last year had a long-term goal of enabling non-chat contexts like microblogging. Like search, these kinds of experiments will require deeply integrated access to the Cwtch database.
## Application Experiments
One kind of experiment we havent touched on yet is additional application functionality, at present we have one main example: Embedded Server Hosting this allows a Cwtch desktop client to setup and manage Cwtch Servers.
This kind of functionality doesnt belong in Cwtchlib as it would necessarily introduce unrelated dependencies into the core library.
This functionality also doesnt belong in the bindings either. They should be as minimal as possible. To that end, we will be moving this functionality out of the bindings and into dedicated repositories which can be managed via an Application Experiment interface.
## Bindings
The last problem to be solved is how to interface experiments with the bindings (libcwtch) and ultimately downstream applications.
We can split the bindings into four core areas:
- **Application Management** - functionality necessary to manage the core Cwtch application e.g. StartCwtch, ReconnectCwtchForeground, Shutdown, CreateProfile etc. This category also include FreePointer which is necessary for safe memory management.
- **Application Experiments** - auxiliary functionality that augments the Cwtch application with new features e.g. Server Hosting etc.
- **Core Profile Management** - core non-experimental functionality that requires a profile e.g. ImportBundle, SendMessage etc. These apis take a handle in addition to the parameters needed to call the underlying function.
- **Experimental Profile Features** auxiliary functionality that augments profiles with additional features e.g. ShareFile, SetProfileImage etc. These apis also take a handle.
The flip side of the bindings is the event bus handing which is responsible for maintaining a queue for the downstream application. This queue provides some filtering and enhancement of events to improve performance. This queue can be moved entirely into Application with only GetAppBusEvent defined and exposed in the bindings.
In an ideal future, all of these bindings could be **generated automatically** from the Cwtchlib interface definitions i.e. there should be no special functionality in the bindings themselves. The generation would need to include C bindings (untyped with automatic checks) and the Dart library calling convention (type safe)
We can define two types of C/Java/Kotlin interface function templates:
- `ProfileMethodName(profilehandle String, args...)` which directly resolves the Cwtch Profile and calls the function.
- `ProfileExperimentalMethodName(profilehandle String, args...)` which checks the current application settings to see if the experiment is enabled, and then resolves the CwtchProfile and calls the function - else errors.
- `ApplicationExperimentalMethodName(args...)` which checks the current application settings to see if the experiment is enabled, and if so, calls the experimental application functionality.
All we need to know from CwtchLib is what methods to export to C bindings, and what template they should use. This can be automatically derived from the context `ProfileInterface` for the first, exported methods of the various `Functionalities` for the second, and `ApplicationExperiment` definitions for the third.
## Timelines and Next Actions
- **Freeze any changes to the bindings interface** - we have made minimal changes to the bindings in the Cwtch 1.9 and 1.10 until we have implemented the proposed changes into cwtchlib.
- As part of Cwtch 1.11 and 1.12 Release Cycles
◦ Implement the Protocol Engine Subsystem Design as outlined above.
◦ Implement the Hooks API.
◦ Move all special behaviour / extra functionalities in the libcwtch-go bindings into cwtchlib with the exception of behaviour related to Application Experiments (i.e. Server Hosting).
◦ Move event handling from the bindings into Application.
◦ Move Application Experiments defined in bindings into their own libraries (or integrate them into existing libraries like cwtch-server) keeping the existing interface definitions.
- Once Automated UI Tests have been integrated into the Cwtch UI Repository:
◦ Write a generate-cwtch-bindings tool that auto generates the libcwtch-go C/Android bindings **and** a dart calling convention library from cwtchlib and any configured application experiments libraries
◦ Port the existing UI app to use the newly generated dart cwtch library (this must wait until we have automated UI testing as part of the build process to ensure that there are no regressions during this process).
◦ At this point the bindings are based off of the generated library and libcwtch-go is deprecated / replaced with automatically generated and versioned bindings.
As these changes are made, and these goals met we will be posting about them here! Subscribe to our [RSS feed](/blog/rss.xml), [Atom feed](/blog/atom.xml), or [JSON feed](/blog/feed.json) to stay up to date, and get the latest on, all Cwtch development.
## Help us get there!
We couldn't do what we do without all the wonderful community support we get, from [one-off donations](https://openprivacy.ca/donate) to [recurring support via Patreon](https://www.patreon.com/openprivacy).
If you want to see us move faster on some of these goals and are in a position, please [donate](https://openprivacy.ca/donate). If you happen to be at a company that wants to do more for the community and this aligns, please consider donating or sponsoring a developer.
Donations of **$5 or more** can opt to receive stickers as a thank-you gift!
For more information about donating to Open Privacy and claiming a thank you gift [please visit the Open Privacy Donate page](https://openprivacy.ca/donate/).
![A Photo of Cwtch Stickers](/img/stickers-new.jpg)
## Appendix A: Special Behaviour Defined by libcwtch-go
The following is an exhaustive list of functionality currently provided by libcwtch-go bindings instead of the cwtchlib:
- Application Settings
- Including Enabling / Disabling Experiment
- ACN Process Management - starting/stopping/restarting/configuring Tor.
- Notification Handling - augmenting/suppressing/augmenting interesting event notifications (primarily for Android)
- Logging Levels - configuring appropriate logging levels (e.g. `INFO` or `DEBUG`)
- Profile Images Helper Functions - handling default profile images for contacts and groups, in addition to looking up custom profile images if the experiment is enabled.
- UI Contact Structures - aggregating contact information for the main Cwtch UI.
- Group Experiment Functionality
- Experiment Gating
- GetServerInfoList
- GetServerInfo
- UI Server Struct Definition
- Server Hosting Experiment Functionality - creating/deleting/managing the server hosting experiment for desktop Cwtch clients.
- "Unencrypted" Profile Handling - replacing a blank password with a default password where the underlying API expects a password but the profile has been designated "unencrypted".
- Image Previews Experiment Handling (eventbus) - automatically starting the downloading of certain file types (when the experiment is enabled).
- Cwtch UI Reconnection Handling (for Android) - restarting various Cwtch subsystems when the UI attempts to reconnect in circumstances where the Android kernel has killed the underlying process.
- Cwtch Profile Engine Activation (eventbus) - starting/stopping ProtocolEngines when requested by the UI, or in response to changes in ACN state.
- UI Profile Aggregation (eventbus) - aggregating information related to Profiles for the UI (e.g. network connection status / unread messages) into a single event.
- File sharing restarts (eventbus)
- UI Event Augmentation - augmenting various internal Cwtch events with information that the UI needs but that isn't directly embedded within the event (e.g. converting `handle` to a `conversation id`). Much of this augmentation is legacy, implemented before recent changes to internal Cwtch structs, and likely can either be removed entirely, or delegated into Cwtch itself.
- Debug Information - special information available to Cwtch debug builds (memory use / active goroutines etc.)