
What is an API
--------------

An API is a set of related methods, accessible over Websocket / HTTP and provided by a single C++ class.  APIs exist on a per-connection basis.

Compiling with hello_api plugin
-------------------------------

We will use the `hello_api` example plugin.  To follow along with the examples, you may enable the `hello_api` example plugin by running the following command and recompiling:

    ln -s ../example_plugins/hello_api external_plugins/hello_api

Publicly available APIs
------------------------

Some APIs are public APIs which are available to clients without login.  These APIs may be specified as follows in the configuration file:

    public-api = database_api login_api hello_api_api

Note, `hello_api` is the name of the plugin and `hello_api_api` is the name of the API it provides.  This slightly confusing naming may be revised in a later version of the plugin.

You may also specify `--public-api` on the command line.

Server configuration note:  If you customize `public-api` configuration, you should set the first two APIs to `database_api` and `login_api`.

For experts:  The reason for recommending `database_api` and `login_api` to be the first two APIs is that many clients expect these to be first two APIs.
Additionally, the string API identifier feature's FC implementation requires a working `get_api_by_name` method to be available on API ID 1, so effectively
any client that uses string API identifiers (as recommended) requires `login_api` to be on API ID 1 even if no client uses the login feature.

Numeric API identifiers
-----------------------

APIs are assigned numeric ID's in the order they are specified as public APIs.  So for example you can access `hello_api_api` over HTTP like this:

    curl --data '{"jsonrpc": "2.0", "params": [2, "get_message", []], "id":1, "method":"call"}' http://127.0.0.1:8090/rpc

The number `2` is the numeric API identifier for `hello_api_api`.  These identifiers are assigned sequentially starting with 0 when the API is registered to the connection.
So effectively this means numeric API identifiers are determined by the order of `hello_api_api` in the `public-api` declaration above.

The `get_api_by_name` method on API 1 (the `login_api`) can be used to query the numeric ID by name:

    curl --data '{"jsonrpc": "2.0", "params": [1, "get_api_by_name", ["hello_api_api"]], "id":1, "method":"call"}' http://127.0.0.1:8790/rpc

String API identifiers
----------------------

Client code that references APIs by their numeric identifiers have to coordinated with server-side configuration.
This is inconvenient, so the API server supports identifying the API by name as well, like this:

    curl --data '{"jsonrpc": "2.0", "params": ["hello_api_api", "get_message", []], "id":1, "method":"call"}' http://127.0.0.1:8090/rpc

It is considered best practice for API clients to use this syntax to reference APIs by their string identifiers.  The reason is that the client becomes robust against
server-side configuration changes which result in renumbering of APIs.

API access control methods
--------------------------

There are three methods to secure the API:

- Limit access to the API socket to a trusted machine by binding the RPC server to localhost
- Limit access to the API socket to a trusted LAN by firewall configuration
- Limit access to particular APIs with username/password authentication

The Hive developers recommend using the first of these methods to secure the API by binding to localhost, as follows:

    rpc-endpoint = 127.0.0.1:8090

Securing specific APIs
-----------------------

The problem with securing APIs at the network level is that there are deployment scenarios where a node may want to have some APIs public, but other APIs private.
The `hived` process includes username/password based authentication to individual APIs.

Since the username/password is sent directly over the wire, you should use a TLS connection when authenticating with username and password.  TLS connection can be achieved by one of two methods:

- Configure an `rpc-tls-endpoint`.
- Configure an `rpc-endpoint` bound to localhost (or a secure LAN), and use an external reverse proxy (for example, `nginx`).  This advanced configuration will not be covered here.

You will need to create a key and certificate for the server in `.pem` format.  Many tutorials for creating SSL certificates exist, and many providers allow creating SSL
certificates signed by widely recognized CA's.  If you have a working `openssl` installation you can use these commands to generate a self-signed certificate:

    openssl req -x509 -newkey rsa:4096 -keyout data/ss-key.pem -out data/ss-cert.pem -days 365
    cat data/ss-{cert,key}.pem > data/ss-cert-key.pem

Then in your `config.ini` you must give the server access to the key and certificate:

    rpc-tls-endpoint = 0.0.0.0:8091
    server-pem = data/ss-cert-key.pem
    server-pem-password = password

When connecting to the API, you can use the `cli_wallet` to honor a specific certificate, individual CA, or CA bundle file with the `-a` option:

    programs/cli_wallet/cli_wallet -s wss://mydomain.tld:8091 -a data/ss-cert.pem

Note that the hostname `mydomain.tld` needs to match the CN field in the certificate.

Now let's protect the `hello_api_api` API to only be usable by user `bytemaster` with password `supersecret`.  Modify the `config.ini` file to make
sure we have the correct plugin which provides the sensitive API, make sure the sensitive API is not accessible in `public-api`, and add an
`api-user` definition to define the access policy:

    enable-plugin = witness account_history hello_api
    public-api = database_api login_api
    api-user = {"username" : "bytemaster", "password_hash_b64" : "T2k/wMBB9BKyv7X+ngghL+MaoubEuFb6GWvF3qQ9NU0=", "password_salt_b64" : "HqK9mAQCkWU=", "allowed_apis" : ["hello_api_api"]}

The values of `password_hash_b64` and `password_salt_b64` are generated by running `programs/util/saltpass.py` and entering your desired password.

Username/password access is only available through websockets, since login is inherently *stateful*.  We can use the `wscat` program to demonstrate the login functionality:

    wscat -c wss://mydomain.tld:8091
    {"jsonrpc": "2.0", "params": ["database_api", "get_dynamic_global_properties", []], "id":1, "method":"call"}
    < (ok)
    {"jsonrpc": "2.0", "params": ["hello_api_api", "get_message", []], "id":2, "method":"call"}
    < (error)
    {"jsonrpc": "2.0", "params": ["login_api", "login", ["bytemaster", "supersecret"]], "id":3, "method":"call"}
    < (ok)
    {"jsonrpc": "2.0", "params": ["hello_api_api", "get_message", []], "id":4, "method":"call"}
    < (ok)

The `cli_wallet` also has the capability to login to provide access to restricted APIs.
