Skip to content

Commit b72667a

Browse files
authored
Merge pull request #94 from Sovereign-Engineering/payment-docs
Add long-form API docs.
2 parents bf586de + cec3659 commit b72667a

23 files changed

Lines changed: 574 additions & 55 deletions

.github/workflows/checks.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ jobs:
88
build_all:
99
name: Build All
1010
runs-on: [nix]
11+
permissions:
12+
# contents: read # to checkout the git repo
13+
pages: write
14+
id-token: write # Required for actions/deploy-pages
1115
steps:
1216
- name: git checkout
13-
uses: actions/checkout@v4
17+
uses: actions/checkout@v6
1418

1519
- name: Build Nix
1620
shell: bash
@@ -20,3 +24,10 @@ jobs:
2024
--no-update-lock-file \
2125
--print-build-logs \
2226
--show-trace
27+
nix build '.#doc' --no-update-lock-file
28+
29+
- uses: actions/upload-pages-artifact@v5
30+
with:
31+
path: result/share/doc/
32+
- uses: actions/deploy-pages@v5
33+
if: ${{ github.ref_name == 'v0' }}

doc/account-creation.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Account Creation
2+
3+
Account creation is implicit.
4+
5+
Follow these steps:
6+
7+
## Generate an Account Number
8+
9+
About account numbers: [Account Number](crate::types::AccountId)
10+
11+
1. Generate a securely random 19-digit integer string (with leading zeros as appropriate).
12+
1. Calculate and append the [Verhoeff check digit](https://en.wikipedia.org/wiki/Verhoeff_algorithm).
13+
14+
## Create the Account
15+
16+
Use the [`AcquireToken`](crate::token::AcquireToken) call to validate the Account Number and availability of new accounts.

doc/auth.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Authentication
2+
3+
Authentication is a simple process.
4+
5+
1. [Create an account](crate::doc::account_creation).
6+
2. [Exchange Account Number for an auth token](crate::token::AcquireToken).
7+
3. [Make requests with the auth token.](crate::token::AcquireToken2Output::auth_token)
8+
9+
While some endpoints don't currently require auth it is recommended to provide an auth token on every request (other than the auth request itself). This may enable more favourable rate limiting, higher quality responses and avoid blocks.

doc/error.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Obscura API Errors
2+
3+
When something goes wrong the API returns an error following HTTP conventions. 4xx for client errors and 5xx for server errors.
4+
5+
The error body will contain an [`ApiErrorBody`](crate::cmd::ApiErrorBody) structure.
6+
7+
In general 4xx errors should not be retried without taking corrective action. 5xx errors can be retried with exponential backoff and jitter. Each error code has documentation about acceptable retry behaviour.

doc/index.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Long-form API Docs
2+
3+
## Disclaimers
4+
5+
### Unstable
6+
7+
This crate and the API behind it are unstable in a number of ways:
8+
9+
1. The API of this crate may be broken at any time.
10+
2. This crate may expose experimental interfaces and services which are completely unsupported. There is no indication of which interfaces these are. Both the Rust APIs and the backing APIs and implementations may change at any time with no notice.
11+
12+
### Support
13+
14+
No support is provided for this code or for the API directly. However, if you are experiencing issues with your Obscura VPN service please contact <support@obscura.net>.

doc/payments.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Payments
2+
3+
This is a guide to how to use the Obscura API to create and fund accounts.
4+
5+
<div class="warning">
6+
7+
This API is only officially supported for our partners. If you are interested in partnering with Obscura please contact <support@obscura.net>.
8+
9+
</div>
10+
11+
## Background Information
12+
13+
This documentation will be critical to understanding how to manage funding.
14+
15+
- [Account Number](crate::types::AccountId)
16+
- [Authentication](crate::doc::auth)
17+
- [Errors](crate::doc::error)
18+
- [Making Requests](crate::doc::requests)
19+
20+
## Example Flows
21+
22+
These are some example flows that highlight how the API can be used for common integration patterns. The links go to reference documentation.
23+
24+
### One-Click Top-Up
25+
26+
If you are a crypto wallet and would like to let your user easily subscribe to Obscura the flow would look something like this:
27+
28+
1. Request the user's Account Number.
29+
1. [Fetch the account info](crate::cmd::GetAccountInfo) to validate the current status. If the account is already [`active`](crate::types::AccountInfo::active) check the [`current_expiry`](crate::types::AccountInfo::current_expiry) field. If it is `None` or far in the future then inform the user about the time left and confirm they do want to top-up now.
30+
1. [Fetch the prices](crate::cmd::ListPrices) and ask the user to select the desired package.
31+
1. Generate an invoice ([Monero](crate::cmd::CreateMoneroTopUp) or [Lightning](crate::cmd::CreateLightningTopUp)) and confirm the crypto price with the user.
32+
1. Record the ID ([Monero](crate::cmd::MoneroTopUpInfo::id) or [Lightning](crate::cmd::LightningTopUpInfo::id)) for the user. This is their payment reference if they ever need to contact support.
33+
1. Pay the invoice.
34+
1. Poll the check status API ([Monero](crate::cmd::CheckMoneroTopUp) or [Lightning](crate::cmd::CheckLightningTopUp)).
35+
1. Let the user know when their account is funded.
36+
37+
### Auto Top-up
38+
39+
While none of the supported currencies support native subscriptions a wallet could help the user avoid forgetting to fund their account.
40+
41+
1. Request the user's Account Number.
42+
1. [Fetch the account info](crate::cmd::GetAccountInfo) to validate the current status.
43+
- If the account is not [`active`](crate::types::AccountInfo::active) then the user will be making the first payment to fund the account immediately.
44+
1. Poll the account info. Daily is a reasonable rate.
45+
1. Wait until [`current_expiry`](crate::types::AccountInfo::current_expiry) is soon (how soon is user preference).
46+
1. Notify the user that their account is expiring, or with user authorization automatically perform a top-up.
47+
1. [Fetch the prices](crate::cmd::ListPrices) and determine the appropriate package.
48+
1. Generate an invoice ([Monero](crate::cmd::CreateMoneroTopUp) or [Lightning](crate::cmd::CreateLightningTopUp)) and confirm the price is within the user's authorized parameters.
49+
1. Pay the invoice.
50+
1. Poll the check status API ([Monero](crate::cmd::CheckMoneroTopUp) or [Lightning](crate::cmd::CheckLightningTopUp)) and notify the user if the payment isn't processed in an appropriate time period.

doc/requests.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Making Obscura API Requests
2+
3+
The Obscura API is served at <https://v1.api.prod.obscura.net/api/>.
4+
5+
## Rust Definitions
6+
7+
The API is described via Rust types in the [obscuravpn-api](https://github.com/Sovereign-Engineering/obscuravpn-api) crate.
8+
9+
These docs contain cross-referenced links to all API calls including the request and response types.
10+
11+
Each API endpoint (other than [the authentication endpoint](crate::doc::auth)) implements the [`Cmd`](crate::cmd::Cmd) trait. You can find a list of all API endpoints by checking [Implementors](crate::cmd::Cmd#implementors) on that page.
12+
13+
This trait specifies the key information of the API call.
14+
15+
- The type `Cmd` is implemented on is the request body.
16+
- For GET queries it is serialized as URL parameters.
17+
- For example: [`ListPrices::code`](crate::cmd::ListPrices::code) becomes `?code=xxx`.
18+
- Otherwise it is serialized as JSON and sent in the request body.
19+
- For example: [`CreateLightningTopUp`](crate::cmd::CreateLightningTopUp) is sent as the request body `{"months":1}`.
20+
- The [`PATH`](crate::cmd::Cmd::PATH) constant indicates the URL path.
21+
- For example [`GetAccountInfo`](crate::cmd::GetAccountInfo) has a `PATH` of `"account"`. Which means that the request should be made to <https://v1.api.prod.obscura.net/api/account>.
22+
- The [`METHOD`](crate::cmd::Cmd::METHOD) constant indicates the HTTP method to use.
23+
- For example [`GetAccountInfo`](crate::cmd::GetAccountInfo) has a `METHOD` of `GET`.
24+
25+
## Other Key Docs
26+
27+
- [Authentication](crate::doc::auth)
28+
- [Errors](crate::doc::error)
29+
- [User-Agent Requirements](crate::doc::user_agent)

doc/user-agent.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# User Agent Requirements
2+
3+
It is important for us to know who is hitting our API so that we can reach out about any concerns or in extreme cases block the requests until issues can be resolved. Having a specific User-Agent may also allow us to exclude you from wider protection measures.
4+
5+
Your user-agent should contain the following info:
6+
7+
1. Your identity. Preferably a URL such as `example.com`. Allows us to get in touch.
8+
1. A version number that increases. This allows us to block or specially handle old clients without blocking fixed clients. Ideally this number increases as you make releases, but it is also fine to just hardcode a number and increment it whenever you make important fixes.
9+
10+
For example: `example.com/1.23` is a great `User-Agent` if you own `example.com`.
11+
12+
**DO NOT** put end-user info into the User-Agent as it will be recorded for a short period of time for operational reasons.

flake.nix

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,35 @@
55
nixpkgs.url = "nixpkgs/nixos-25.05";
66
};
77

8-
outputs = { self, crane, nixpkgs, flake-utils }:
8+
outputs = { self, crane, nixpkgs, flake-utils }@inputs:
99
flake-utils.lib.eachDefaultSystem (system:
1010
let
11-
pkgs = import nixpkgs { inherit system; };
12-
craneLib = crane.mkLib pkgs;
11+
nixpkgs = import inputs.nixpkgs { inherit system; };
12+
inherit (nixpkgs) lib pkgs;
13+
craneLib = crane.mkLib nixpkgs;
1314

1415
depsArgs = {
15-
src = craneLib.cleanCargoSource self;
16+
src = lib.fileset.toSource {
17+
root = ./.;
18+
fileset = lib.fileset.unions [
19+
./about.toml
20+
./Cargo.lock
21+
./Cargo.toml
22+
./doc
23+
./rust-toolchain.toml
24+
./rustfmt.toml
25+
./src
26+
];
27+
};
1628
strictDeps = true;
1729
};
1830
cargoArgs = depsArgs // { cargoArtifacts = craneLib.buildDepsOnly depsArgs; };
31+
32+
doc = craneLib.cargoDoc (cargoArgs // { RUSTDOCFLAGS = "-Dwarnings"; });
1933
in {
2034
checks = {
35+
inherit doc;
36+
2137
actionlint = pkgs.runCommand "actionlint" { nativeBuildInputs = [ pkgs.actionlint ]; } ''
2238
actionlint -config-file ${./.github}/actionlint.yml ${./.github}/**/*.yml
2339
touch "$out"
@@ -52,5 +68,7 @@
5268
export OBSCURA_MAGIC_IN_NIX_SHELL=1
5369
'';
5470
};
71+
72+
packages = { inherit doc; };
5573
});
5674
}

src/cmd/account/delete.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ impl Cmd for DeleteAccount {
1111
type Output = DeleteAccountOutput;
1212

1313
const METHOD: http::Method = http::Method::DELETE;
14-
const PATH: &'static str = super::PATH;
14+
const PATH: &'static str = "account";
1515
}

0 commit comments

Comments
 (0)