About Bridges
Date | Responsible | Changes |
---|---|---|
December 15, 2022 | @Željko Rumenjak | Initial version |
January 20, 2023 | @Branko Durdevic | Bridge interface update |
February 15, 2023 | @Tomislav Herman | Update of naming, introduced ‘entry’ which represents credit or debit. Updated JSON request examples according to new schema: prepare now has ‘schema’ and doesn’t have ‘select’, commit/abort has only ‘handle’ and ‘action’. |
February 17, 2023 | @Tomislav Herman | Added description of standard endpoints for generic rest interface of the bridge. Added diagram which shows two phase commit entities and endpoints in detail. |
March 7, 2023 | @Filip Herceg | • Refactored signer.schema → signer.format • Changed bearer action to read |
March 7, 2023 | @Omar Monterrey | Updated schema of bridge payloads (target, source and symbols are now objects) |
March 9, 2023 | @Tomislav Herman | Updated intent access item to match the specification for nested signer in access rule. Added intent to commit and abort requests. Added example of custom.status and custom.moment to intent signature. |
May 22, 2023 | @Omar Monterrey | Removed bridge.schema property |
October 24, 2023 | @Luis Fidelis | • Removed bridge.config.signer property • Added bridge.schema property • Added and described bridge.traits property • Detailed anchor and domains endpoints |
March 15, 2024 | @Tomislav Bradaric | Added docs for Claim grouping |
March 18, 2024 | @Tomislav Bradaric | Added docs for bridge retries, as well as note about future release of an endpoint for bridge that is catching up to speed up delivery of entries. |
March 25, 2024 | @Omar Monterrey | Replaced note of future endpoint for events redelivery with an actual callout after implementing it. |
June 5, 2024 | @Omar Monterrey | Added Table of contents and section “Endpoints enabled for each trait” |
December 3, 2024 | @Omar Monterrey | Added documentation for Filtering traits and Other traits endpoints |
Bridge represents a ledger configuration record that is used to register remote services with the ledger. This is an optional record in most cases, since remote service calls can usually be specified inline, for example actions in effects, aspects, etc. An example of a bridge is an integration service that connects with a banking core in order to perform debit and credit operations in response to ledger balance movements. Other use cases include services which host webhooks in order to subscribe to ledger events and react to them.
Bridges are here to centralize this concept and make it easier to manage external integrations. The most important value of a bridge is that it is possible to easily manage all integrations in one place. Another benefit is avoiding the need to repeat the same information in multiple places. Data like security configuration, base URLs and similar can be registered only in a bridge record, and you can reference this configuration much more easily this way.
Bridges are created the same way as all other ledger records, they have their handle and some additional properties that is specific to this record type. Specific properties of bridges are:
- schema - defines the communication schema used by the bridge, for example
rest
- config - a base config that is shared across all bridge functionalities, defines the base URL.
- secure - defines security mechanisms for accessing the bridge, this includes OAuth, MTLS, and similar configurations
Here is an example of registering a bridge that exposes REST API endpoints:
💡 Note that the server
url has /v2
prefix in path which represents the current version of ledger protocol. It is the recommended to use versioning of rest endpoints on bridge so that newer versions of protocol can be easily supported in the future.
All examples below will use /v2
path prefix on bridge endpoints, which means that registered bridge server
configuration should have this prefix. Ledger will not prepend it automatically.
By creating a Bridge that uses the rest
schema, the interface below has to be exposed on the registered URL.
Rest interface consists of these types of endpoints:
- Record endpoints
POST /v2/<record-plural>
for creating the record on bridge side, record signed by ledger key is received in the HTTP bodyPUT /v2/<record-plural>/:handle
for updating the record by handle or creating if it doesn’t exist yet, new state of record signed by ledger key is received in HTTP body
- Command endpoints
POST /v2/<record-plural>/:handle/<action>
for executing an action on the record, command record which contains the handle and action signed by ledger key is sent in HTTP body
- Event endpoints
POST /v2/effects/:handle
used to receive events which originate from the registered effect withhandle
, see details about effects
- Anchor endpoints
GET /v2/anchors
used to resolve anchors on the bridge side. This endpoint can only be called if the bridge implementsanchors
trait. See details About AnchorsPOST /v2/anchors/!lookup
used to resolve anchors on the bridge side with additional parameters in body. This endpoint can only be called if the bridge implementsanchors
trait
- Domain endpoints
GET /v2/domains
used to resolve wallet domains on the bridge side. This endpoint can only be called if the bridge implementsdomains
trait. See details About Wallets
Bridge traits
Traits defines features implemented by the bridge. It can be informed either when creating a bridge or updating it, and should be sent in the data
of bridge payload. It's optional and accepts a list of strings, options are:
credits
: bridge implements credits operations. Called in two phase commit process when receiving money.debits
: bridge implements debits operations. Called in two phase commit process when sending money.statuses
: bridge implements intent statuses receiver. Called when intent receives an update.anchors
: bridge implements anchors handler. Associated with alias directory process.domains
: bridge implements domains handler. Associated with alias directory process.events
: bridge implements events handler. Events are related to ledger effects i.e balance received. See How to register an effect for details.
If traits
is not present in bridge record, all the traits above are enabled by default.
Endpoints enabled for each trait
Bridge traits will let the ledger know that the bridge properly implement some endpoint. If the bridge record “traits” property is set but does not have some trait, the endpoints under that trait won't be called.
For example, if a bridge has ["debits"]
as the traits property, it will be called over for debits but not for credits. For credits only default ledger validations (Like balances and permissions) will be executed, without calling prepare or commit on the bridge.
Endpoint | Description | Trait |
---|---|---|
POST /credits | Credit prepare | credits |
POST /credits/:handle/commit | Credit commit | credits |
POST /credits/:handle/abort | Credit abort | credits |
POST /debits | Debit prepare | debits |
POST /debits/:handle/commit | Debit commit | debits |
POST /debits/:handle/abort | Debit abort | debits |
PUT /intents/:handle | Intent status update | statuses |
GET /wallets/:handle/anchors | Anchors query | anchors |
POST /wallets/:handle/anchors/!lookup | Anchors lookup | anchors |
GET /wallets/:handle/domains | Domains query | domains |
POST /effects/:handle | Effect signal triggered | effects |
Filtering traits
Sometimes you don’t want to participate in the Two Phase Commit process for all intents, or sometimes you don’t want to receive intent updates for all intents. For these scenarios, you can specify traits by passing an object with properties method
and filter
instead of simply the method.
Filters are available to all traits except for domains
, because they are used for a GET request with no body, and effects
, because you can filter each effect’s payload when creating the effect record.
The filter targets the data
property of the event’s payload, see the “Two phase commit endpoints” and “Other traits endpoints” for example payloads of different requests.
For example, if we want our bridge to participate in all credits
, in debits
when the intent schema is not settle
and receive intents status updates for intents which has any claim with an amount greater than or equal to 100
, we can define our bridge like this:
Filters for credits
and debits
are only applied during prepare
phase
On filters, you should target the property you want to filter using dot notation. Mongo-like operators are supported.
Claim grouping
By default, when an intent with N claims is created, for each claim where a bridge is the configured bridge of the resolved source or target wallet, the bridge will receive an entry like prepare
(debit
or credit
), commit
or abort
as described in the following chapter Two phase commit endpoints
.
For intents with several claims, a bridge might receive numerous unordered notifications. This can complicate processing.
Optionally, a bridge can be configured so that the ledger groups its calls to the bridge.
To do so, set the bridge's configuration under debits.claims.groupBy
or credits.claims.groupBy
to wallet
or address
, so a config might look like this:
Example bridge config
There are two types of grouping:
- grouping by
address
- Grouped by the handle set as source/target. For example, for two claims in a
send
intent wheresource
for both claims ismy-wallet
buttarget
is different for both claims, the source bridge will receive only one groupedprepare
instead of two single ones, while target bridge will still receive two singleprepare
calls.
- Grouped by the handle set as source/target. For example, for two claims in a
- grouping by
wallet
- Grouped by the handle of the resolved source/target wallet. For example, for two claims in a
send
intent wheresource
for one ismy-wallet
and the other isother-wallet
, but where both wallets get resolved to their shared parentparent-wallet
, the source bridge will receive only one groupedprepare
instead of two single ones
- Grouped by the handle of the resolved source/target wallet. For example, for two claims in a
Retries for requests from ledger to bridge
When ledger communicates with a bridge, it will use retry behavior in case the bridge fails to respond with a successful status code. These requests may include operations such
as prepare
, commit
, abort
, status
, or delivering a webhook for an effect.
The default settings for this retry mechanism are as follows unless specified differently in the documentation for a specific bridge entry. These are configurable server-wide through environment variables, individually for each type of retry (prepare
, commit
, abort
, status
, effect webhook delivery
).
- Initial Delay: The initial delay between the first request and the first retry is set to 1 second.
- Backoff Coefficient: Each subsequent retry will occur after a delay that is 20% longer than the previous one.
- Maximum Delay: The delay between retries will not exceed 1 hour.
The maximum delay means that once the delay between retries reaches 1 hour, all subsequent retries will continue to occur every hour until a successful response is received or the operation is otherwise terminated.
In extreme cases, the ledger may employ other criteria for terminating the retry sequence, such as a maximum number of retries or total timeout, as defined in the operational policies or specific bridge documentation.
When a bridge is back online after being offline for a period of time, it can call the POST /v2/bridges/:handle/activate
endpoint which will speed up the events delivery towards that bridge by rescheduling them with no delay. That way the bridge does not have to wait up to 1 hour to fully catch up.
Two phase commit endpoints
Following concrete endpoints endpoints will be called by Ledger as part of the two phase commit procedure described here. The Ledger side of the two phase commit interface consists of regular Ledger endpoints called with specific payloads. The endpoints for credit and debit operations are symmetric, the only difference is in the operations that need to be done on the backend.
POST /v2/credits
This endpoint is called when Ledger requests the Bridge to prepare a credit entry. The Bridge should save the entry, respond with 202 Accepted and then process the request.
Example body
POST /v2/credits/:handle/commit
This endpoint is called when Ledger requests the Bridge to commit previously prepared credit entry. The Bridge should load previously saved entry, respond with 202 Accepted and then process the request.
Example body
POST /v2/credits/:handle/abort
This endpoint is called when Ledger requests the Bridge to abort previously prepared credit entry. The Bridge should load previously saved entry, respond with 202 Accepted and then process the request.
Example body
POST /v2/debits
This endpoint is called when Ledger requests the Bridge to prepare a debit entry. The Bridge should save the entry, respond with 202 Accepted and then process the request. The request is analogous to POST /credits.
POST /v2/debits/:handle/commit
This endpoint is called when Ledger requests the Bridge to commit previously prepared debit entry. The Bridge should load previously saved entry, respond with 202 Accepted and then process the request. The request is analogous to POST /debits/:handle/commit.
POST /v2/debits/:handle/abort
This endpoint is called when Ledger requests the Bridge to abort previously prepared debit entry. The Bridge should load previously saved entry, respond with 202 Accepted and then process the request. The request is analogous to POST /debits/:handle/abort.
PUT /v2/intents/:handle
This endpoint is used by the Ledger to report intent status changes to the Bridge. It will be called with an Intent record and the Bridge should insert it into the database if it does not already exist or update it with the new value if it does. This can be used to follow the status of Intents as they are processed, for example when an Intent is fully processed, it will end up in either completed
or rejected
status.
Example body
Other traits endpoints
GET /v2/wallets/:handle/anchors
This endpoint is called when anchors are queried for a wallet that has a bridge with trait anchors
linked. Since it’s a GET request, it has no payload.
It should return a signed body with valid anchors
Example expected response
POST /v2/wallets/:handle/!lookup
This endpoint is called when anchors are looked up for a wallet that has a bridge with trait anchors
linked. The expected response is the same as the one for the endpoint GET /v2/wallets/:handle/anchors, the difference is that the named query !lookup allows payload to be sent.
Example body
GET /v2/wallets/:domain
This endpoint is called when domains are queried for a wallet that has a bridge with trait domains
linked. Since it’s a GET request, it has no payload.
It should return a signed body with valid domains.