Designed to replace the old protobuf-based ricochet channels, Tapir provides a framework for building anonymous applications.

It is divided into a number of layers:

  • Identity - An ed25519 keypair, required to establish a Tor v3 onion service and used to maintain a consistent cryptographic identity for a peer.
  • Connections - The raw networking protocol that connects two peers. Connections are so far only defined over Tor v3 Onion Services (see: connectivity)
  • Applications - The various logic that enables a particular information flow over a connection. Examples include shared cryptographic transcripts , authentication, spam guards and token based services. Applications provide Capabilities which can be referenced by other applications to determine if a given peer has the ability to use a given hosted application.
  • Application Stacks - A mechanism for connecting more than one application together, e.g. authentication depends on a shared cryptographic transcript , and the main cwtch peer app is based on the authentication application.



An ed25519 keypair, required to establish a Tor v3 onion service and used to maintain a consistent cryptographic identity for a peer.

  • InitializeIdentity - from a known, persistent keypair: \(i,I\)
  • InitializeEphemeralIdentity - from a random keypair: \(i_e, I_e\)


Transcript App

Dependencies: None

Initializes a Merlin-based cryptographic transcript that can be used as the basis of higher level commitment-based protocols

Transcript app will panic if an app ever tries to overwrite an existing transcript with a new one (enforcing the rule that a session is based on one, and only one, transcript.)

Authentication App

  • Dependencies: Transcript App
  • Capabilities Granted: AuthenticationCapability
  • Capabilities Required: None

Engages in an ephemeral triple-diffie-hellman handshake to derive a unique, authenticated session key.


The merlin transcript derived challenge is based on all the messages sent in the auth flow (and any that were sent prior to the Auth App)

// Derive a challenge from the transcript of the public parameters of this authentication protocol
transcript := ea.Transcript()
transcript.AddToTranscript("outbound-hostname", []byte(outboundHostname))
transcript.AddToTranscript("inbound-hostname", []byte(inboundHostname))
transcript.AddToTranscript("outbound-challenge", outboundAuthMessage)
transcript.AddToTranscript("inbound-challenge", inboundAuthMessage)
challengeBytes := transcript.CommitToTranscript("3dh-auth-challenge")


The client connection is guaranteed to possess the long term identity of the server connection through the properties of the underlying tor v3 onion connection.

As such if the server attempts to send a different long term identity to the client we can detect it and terminate the authentication protocol early.

Token App

Dependencies: Transcript App

  • Capabilities Granted: HasTokensCapability
  • Capabilities Required: None (implicitly guarded)

Allows the client to obtain signed, blinded tokens for use in another application.

While this application has no explicit requirement for any given capability, we expect it to be protected via a preceeding app in an ApplicationChain e.g.

powTokenApp := new(applications.ApplicationChain).
        ChainApplication(new(applications.ProofOfWorkApplication), applications.SuccessfulProofOfWorkCapability).
        ChainApplication(tokenApplication, applications.HasTokensCapability)


  • No direct testing (tested via integration tests and unit tests)

Known Risks

Impersonation of Peers

Status: Mitigated

By default, tor v3 onion services only provide one-way authentication, that is the client can verify a metadata resistant connection to the server but the server obtained no information about the client.

Tapir provides a peer-to-peer interface over this client-server structure through the Authentication application.

The Authentication application implements a 3-way, ephemeral diffie-hellman handshake to generate a shared session key. Once generated this session key is used to encrypt all traffic between the two peers for the duration of the session.

The session key is used to encrypt a challenge derived from the shared cryptographic transcript (based on merlin)

Only if all the above checks pass is the connection maintained open - otherwise the peer that detects a failure closes the connection.

Double Connections

Status: Mitigated

Because of the one-way authentication provided by Tor onion services there is a window between connection instantiation and the finalization of authentication when two valid connections can occur between the same two peers.

While these vestigial connections are not harmful, they do have the potential to confuse users and interfaces. To avoid ambiguity Tapir attempt to detect and close duplicate connections through a number of rules:

  1. If a connection open is attempted to a hostname that already has an open connection the connection attempt is aborted.
  2. After authentication the lookup happens again, and if another connection is found the newest connection is closed.

There is a small chance both peers will close their initiated connections if they also happen to start the connection attempt at exactly the sametime . This should be exceedingly rare in practice, and is further mitigated by an exponential backoff of connection retries by the ui

Finally, the Tapir interfaces WaitForCapabilityOrClose and GetConnection are aware of the potential for duplicate connections and have logic that allows the handling of such instances (such as returning an error when they are found allowing a handling application to retry the request if a connection with a given capability isn't returned)

Ephemeral Connections

Occasionally it is desirable to have a peer connect to a service without using their long term identity (e.g. in the case of connecting to a Cwtch Server).

In this case we want to enable a convenient way to allow connecting with an ephemeral identity.

It turns out that doing this securely requires maintaining a completely separate set of connections and applications in order to avoid side channels caused by duplicate connections handling.

As such the Cwtch Protocol Engine maintains two disctinct connection pools, one for avowed connections and another for ephemeral connections. All connections to known Cwtch Servers are made through the ephemeral pool.

Testing Status

Tapir features a number of well-defined integration tests which exercise not only the ideal case of two well-formed peers authenticating and messaging each other, but also a malicious peer attempting to bypass authentication.

In addition, unit tests are defined for a number of the specified applications (including Authentication) and many of the cryptographic primitives.

All tests are also run with the -race flag which will cause them to fail if race conditions are detected. Both integration tests and unit tests are run automatically for every pull request and main branch merge.