Nuts documentation
“Hello World” on Docker
The simplest way to spin up the Nuts stack is by using the setup provided by nuts-network-local. The setup is meant for development purposes and starts a Nuts node, “Demo EHR”, “Registry Admin Demo” for administering your vendor and care organizations and a HAPI server to exchange FHIR data.
To get started, clone the repository and run the following commands to start the stack:
cd single
docker compose pull
docker compose up
After the services have started you can try the following endpoints:
Registry Admin Demo login (default password: “demo”).
Demo EHR login (default password: “demo”).
Running on Docker
If you already use Docker, the easiest way to get your Nuts Node up and running for development or production is using Docker. This guide helps you to configure the Nuts node in Docker. To use the latest master build use nutsfoundation/nuts-node:master (for production environments it’s advisable to use a specific version).
First determine the working directory for the Nuts node which will contain configuration and data. These which will be mounted into the Docker container. Follow the configuration to setup the configuration of your node.
Mounts
Using this guide the following resources are mounted:
Readonly PEM file with TLS certificate and private key. They can be separate but in this example they’re contained in 1 file.
Readonly PEM file with TLS truststore for the particular network you’re connecting to.
Readonly nuts.yaml configuration file.
Data directory where data is stored.
Docker run
If you want to run without Docker Compose you can use the following command from the working directory:
docker run --name nuts -p 5555:5555 -p 1323:1323 \
--mount type=bind,source="$(pwd)"/certificate-and-key.pem,target=/opt/nuts/certificate-and-key.pem,readonly \
--mount type=bind,source="$(pwd)"/truststore.pem,target=/opt/nuts/truststore.pem,readonly \
--mount type=bind,source="$(pwd)"/nuts.yaml,target=/opt/nuts/nuts.yaml,readonly \
--mount type=bind,source="$(pwd)"/data,target=/opt/nuts/data \
-e NUTS_CONFIGFILE=/opt/nuts/nuts.yaml \
nutsfoundation/nuts-node:master
This setup uses the following nuts.yaml configuration file:
datadir: /opt/nuts/data
network:
truststorefile: /opt/nuts/truststore.pem
certfile: /opt/nuts/certificate-and-key.pem
certkeyfile: /opt/nuts/certificate-and-key.pem
bootstrapnodes:
- example.com:5555
Note
The command above uses pwd and bash functions, which do not work on Windows. If running on Windows replace it with the path of the working directory.
You can test whether your Nuts Node is running properly by visiting http://localhost:1323/status/diagnostics. It should display diagnostic information about the state of the node.
Docker Compose
Copy the following YAML file and save it as docker-compose.yaml in the working directory.
version: "3.7"
services:
nuts:
image: nutsfoundation/nuts-node:master
environment:
NUTS_CONFIGFILE: /opt/nuts/nuts.yaml
ports:
- 5555:5555
- 1323:1323
volumes:
- "./certificate-and-key.pem:/opt/nuts/certificate-and-key.pem:ro"
- "./truststore.pem:/opt/nuts/truststore.pem:ro"
- "./nuts.yaml:/opt/nuts/nuts.yaml:ro"
- "./data:/opt/nuts/data:rw"
Start the service:
docker-compose up
Running the native binary
The Nuts executable this project provides can be used to both run a Nuts server (a.k.a. node) and administer a running node remotely. This guide explains how to run a Nuts node using the native binary.
Building
Since no precompiled binaries exist (yet), you’ll have to build the binary for your platform.
First check out the project using:
git clone https://github.com/nuts-foundation/nuts-node
cd nuts-node
Then create the executable using the make command:
make build
Or if make is not available:
go build -ldflags="-w -s -X 'github.com/nuts-foundation/nuts-node/core.GitCommit=GIT_COMMIT' -X 'github.com/nuts-foundation/nuts-node/core.GitBranch=GIT_BRANCH' -X 'github.com/nuts-foundation/nuts-node/core.GitVersion=GIT_VERSION'" -o /path/to/nuts
Make sure GIT_COMMIT, GIT_BRANCH and GIT_VERSION are set as environment variables. These variables help identifying an administrator and other nodes what version your node is using. If this isn’t important then replace GIT_COMMIT with 0, GIT_BRANCH with master and GIT_VERSION with undefined.
Starting
Start the server using the server command:
nuts server
Now continue with the configuration.
Setting up your node for a network
After you managed to start your node using either docker or native it’s time to connect to a network.
Prerequisites
The following is needed to connect a Nuts node to a network:
A runnable node.
A network you want to join.
A TLS client- and server certificate which is accepted by the other nodes in the network (e.g. PKIoverheid).
The public address of one or more remote nodes you’d like to use as bootstrap nodes.
A node identity (node DID) to identify yourself in the network, so you can send/receive private transactions.
Networks
A network contains of a set of nodes who can all communicate with each other. To make this possible, each of the nodes must meet the following requirements:
Share a common set of trusted Certificate Authorities.
Use a certificate issued by one of the CAs.
The network transactions share the same root transaction.
Use and accept network protocol versions from an agreed upon set.
There are 3 official Nuts networks:
development where new features are tested. Nodes will generally run the newest (not yet released) version of the Nuts node.
stable for integrating your software with Nuts and testing with other vendors. Nodes will generally run the latest released version (or at least a recent one).
production for production uses. Connecting to this network involves PKIoverheid certificates and outside the scope of this tutorial.
Node TLS Certificate
Before you can join a network, your node needs a certificate from the correct Certificate Authority (CA). The two development and`stable` networks are open for everyone to join. Contrary to the production network (where we will be using a real Certificate Authority like PKIoverheid) the CA certificate and private key for these networks are available on github. This way you can generate your own certificate.
To generate the certificate for your own node you need the https://github.com/nuts-foundation/nuts-development-network-ca
repository. It contains handy scripts and the needed key material. For more information how to use, consult the README
Your node only accepts connections from other nodes which use a certificate issued by one of the trusted CAs. Trusted CAs are using a truststore file. The truststore is a PEM file which contains one or more certificates from CAs which the network participants all decided on to trust. To learn more about how a Nuts network uses certificates, see the specification RFC008.
To generate certificates for the development network perform the following steps:
git clone https://github.com/nuts-foundation/nuts-development-network-ca
cd nuts-development-network-ca
./issue-cert.sh development nuts.yourdomain.example
This results in 3 files:
nuts.yourdomain.example-development.key
The private key for the node.nuts.yourdomain.example-development.pem
The certificate for the node.truststore-development.pem
The truststore for this (development) network.
Bootstrap nodes
A bootstrap node is just a normal Nuts node which is available for other nodes to connect to. When you want to join a network, you must approach another network participant and ask for its public (gRPC) endpoint. After connecting, you receive a copy of the current state of the network. These transactions contain endpoints of other nodes. After a reboot, your node will try to connect to other nodes discoverd in the network. Your node will have to connect to the bootstrap node’s gRPC endpoint which is configured on port 5555
by default.
Consult the community on Slack in the #development
channel to find out which public bootstrap nodes are available to connect to your network of choice.
Configuring
Configure the bootstrap nodes using
network.bootstrapnodes
.Configure TLS using
network.certfile
,network.certkeyfile
andnetwork.truststorefile
.
See configuration reference for a detailed explanation on how to exactly configure the Nuts node.
Note
You can start the node without configuring the network, but it won’t connect and thus exchange data with other nodes. You’ll have a private network with one single node. Perfect for local development, but a bit lonely.
Node Identity
Certain data (e.g. private credentials) can only be exchanged when a peer’s DID has been authenticated.
To make sure other nodes can authenticate your node’s DID you need to configure your node’s identity,
and make sure the DID document contains a NutsComm
service that matches the TLS certificate.
Your node identity is expressed by a DID that is managed by your node, also known as your vendor DID.
So make sure you have created a DID specific for your nodes and configure it as network.nodedid
(see configuration reference).
Then you make sure the associated DID Document contains a NutsComm
endpoint,
where the domain part (e.g. nuts.nl
) matches (one of) the DNS SANs in your node’s TLS certificate.
See “Node Discovery” below for more information on registering the NutsComm
endpoint.
Note
After registering nodedid
you need to reboot your node in order have your connections authenticated, which is required to receive private transactions.
Note
Multiple nodes may share the same DID, if they’re governed by the same organization (e.g., clustered setups).
YAML Configuration File
If you’re using a YAML file to configure your node, the following snippet shows an example for the network related configuration:
network:
truststorefile: /path/to/truststore-development.pem
certfile: /path/to/nuts.yourdomain.example-development.pem
certkeyfile: /path/to/nuts.yourdomain.example-development.key
nodedid: did:nuts:123
bootstrapnodes:
- nuts-development.other-service-provider.example:5555
Node Discovery
To allow your Nuts node to be discovered by other nodes (so they can connect to it) and be able to receive private transactions, you need to register a NutsComm
endpoint on your vendor DID document.
The NutsComm
endpoint contains a URL to your node’s public gRPC service,
and must be in the form of grpc://<host>:<port>
.
E.g., if it were to run on nuts.nl:5555
, the value of the NutsComm
endpoint should be grpc://nuts.nl:5555
You can register the NutsComm
endpoint by calling addEndpoint
on the DIDMan API:
POST <internal-node-address>/internal/didman/v1/did/<vendor-did>/endpoint
{
"type": "NutsComm",
"endpoint": "grpc://nuts.nl:5555"
}
Care Organizations
The DID documents of your care organizations you (as a vendor) want to expose on the Nuts network need to be associated
with your vendor’s DID document through the NutsComm
endpoint.
Its recommended to register the actual NutsComm
endpoint on your vendor DID document (as explained in the previous section),
and register a reference to this endpoint on the DID documents of your vendor’s care organizations:
POST <internal-node-address>/internal/didman/v1/did/<care-organization-did>/endpoint
{
"type": "NutsComm",
"endpoint": "<vendor-did>/serviceEndpoint?type=NutsComm"
}
Getting Started on customer integration
This getting started manual assumes a vendor sells services to its customers. The vendor manages the presence of those customers on the Nuts network through the Nuts registry. It’s very likely the vendor has software to manage customer environments. We’ll call it a CRM where the customers part relates to organizations and not people. This does not mean that a stand-alone installation isn’t supported. In that case the vendor and organization are the same.
The Nuts registry enables service discovery for organizations. The registry identifies organizations through their DID. The DIDs are unique identifiers which are generated when the organization is registered in the Nuts registry. After creation of the DID the CRM should store and map it to its customer record, so it can refer to it when updating the customer’s DID Document and issue Verifiable Credentials.
All APIs used in the following chapters are documented at the API page. Open API Spec files are available for generating client code.
Vendor integration
As a vendor, you’re in power of:
running a Nuts node
handling organization key material
updating the organization DID Document
defining service endpoints
issuing name credentials for organizations
trusting other vendors
The last three points require a setup where a vendor DID is created. This DID will act as the controller of all organization DID Documents. This will allow for reuse of service endpoints and issuance of Verifiable Credentials. Since every DID issuing Verifiable Credentials must be trusted individually, it’s easier for other vendors when the vendor uses a single DID for issuing credentials.
Create and store a vendor DID
Your CRM must store the DIDs created for your vendor and your customers. A DID is a string similar to:
did:nuts:2mF6KT6eiSx5y2fwTP4Y42yMUh91zGVkbu4KMARvCJz9
The DID we’re about to create is your vendor DID. It will be used in the all of the next steps.
For the API calls that will need to be made to the Nuts node, we’ll use <internal-node-address>
as the address where the internal API’s are exposed.
Consult the configuration reference on how to configure the node address.
POST <internal-node-address>/internal/vdr/v1/did
{
"selfControl": true,
"keyAgreement": true,
"assertionMethod": true,
"capabilityInvocation": true
}
The request above instructs the node to create a new DID and DID Document. The DID Document will be published to all other nodes.
The node will generate a new keypair and store it in the crypto backend.
The options above will instruct the node to allow the DID Document to be changed by itself (selfControl = true AND capabilityInvocation = true
) and that the DID can be used to issue credentials (assertionMethod = true
).
If all is well, the node will respond with a DID Document similar to:
{
"@context": [ "https://www.w3.org/ns/did/v1" ],
"id": "did:nuts:2mF6KT6eiSx5y2fwTP4Y42yMUh91zGVkbu4KMARvCJz9",
"verificationMethod": [
{
"id": "did:nuts:2mF6KT6eiSx5y2fwTP4Y42yMUh91zGVkbu4KMARvCJz9#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw",
"controller": "did:nuts:2mF6KT6eiSx5y2fwTP4Y42yMUh91zGVkbu4KMARvCJz9",
"type": "JsonWebKey2020",
"publicKeyJwk": {
"crv": "P-256",
"x": "38M1FDts7Oea7urmseiugGW7tWc3mLpJh6rKe7xINZ8",
"y": "nDQW6XZ7b_u2Sy9slofYLlG03sOEoug3I0aAPQ0exs4",
"kty": "EC"
}
}],
"capabilityInvocation": [
"did:nuts:2mF6KT6eiSx5y2fwTP4Y42yMUh91zGVkbu4KMARvCJz9#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw"
],
"assertion": [
"did:nuts:2mF6KT6eiSx5y2fwTP4Y42yMUh91zGVkbu4KMARvCJz9#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw"
],
"keyAgreement": [
"did:nuts:2mF6KT6eiSx5y2fwTP4Y42yMUh91zGVkbu4KMARvCJz9#_TKzHv2jFIyvdTGF1Dsgwngfdg3SH6TpDv0Ta1aOEkw"
],
"service": []
}
The id
at the top level needs to be extracted and stored as your vendor DID.
In the example above this would be did:nuts:2mF6KT6eiSx5y2fwTP4Y42yMUh91zGVkbu4KMARvCJz9
.
The DID Document shouldn’t be stored since the Nuts node will do this for you.
Setting vendor contact information
Things can go wrong: a node is misbehaving or a DID Document is conflicted. If the node operator is not resolving the problem it’s extremely convenient if others can contact the node operator and relay the problem. For this use-case, Nuts supports the registration of node contact information. The contact information will be added to a DID Document as a service. A convenience API is available to add the contact information to a DID Document. The vendor DID should be used for this.
PUT <internal-node-address>/internal/didman/v1/did/<did>/contactinfo
{
"name": "vendor X",
"phone": "06-12345678",
"email": "info@example.com",
"website": "https://example.com"
}
Where <did>
must be replaced with the vendor DID.
Adding endpoints
As a vendor you’ll probably be hosting different services at various stages. A Nuts node API is available to easily add/remove the endpoints for these services. Registering services is a required step since the services that will be registered for organizations will make use of these services.
POST <internal-node-address>/internal/didman/v1/did/<did>/endpoint
{
"type": "example-production-api",
"endpoint": "https://api.example.com"
}
Where <did>
must be replaced with the vendor DID. The type
may be freely chosen and is used as reference in the organization services.
The endpoint
must be a valid endpoint (this differs per type of service).
For some services this could be a base-url. If this is the case, the bolt description will note this.
Organization integration
Each organization (or customer) must be registered with its own DID and DID Document. The vendor CRM should make it possible to store a DID for each organization. Requests that are made in the context of the organization will use the private key of the organization. To easily control the DID Document of an organization, the vendor will be the controller.
Create and store a customer DID
A DID can be created like the vendor DID:
POST <internal-node-address>/internal/vdr/v1/did
{
"selfControl": false,
"controllers": [<did>],
"assertionMethod": true,
"capabilityInvocation": false
}
Where <did>
must be replaced with the vendor DID.
The body for creating an organization DID differs from the vendor DID in the fact that the vendor DID is in control of the newly generated DID Document.
The assertionMethod
is still true since it’ll allow for the generation of access-tokens in the context of the organization.
The result is similar to the output of the vendor DID creation.
In this case the id
must also be extracted and stored within the vendor CRM for the right organization.
Issue a Nuts Organization Credential
After registering an organization, its presence on the network and in the Nuts registry is now only a DID.
In order for other organizations to find the correct DID and connected services, credentials should be issued and published over the network.
For this, the NutsOrganizationCredential can be issued by any vendor.
A NutsOrganizationCredential contains the name
of the organization and the city
where this name is registered as organization.
The combination of those should be unique (since duplicate names within a sector is disallowed).
A credential can be issued with the following call:
POST <internal-node-address>/internal/vcr/v2/issuer/vc
{
"type": "NutsOrganizationCredential",
"issuer": "<issuer-did>",
"credentialSubject": {
"id": "<holder-did>",
"organization": {
"name": "<name>",
"city": "<city>"
}
},
"visibility": "public"
}
Where <issuer-did>
must be replaced with the vendor DID, <holder-did>
must be replaced with the organization DID,``<name>`` and <city>
must be replaced with the correct information.
The API will respond with the full Verifiable Credential. It’s not required to do anything with that since issued credentials can be found again.
Trusting other vendors as issuer
A node operator must not blindly trust all the data is published over the network. Before credentials can be found, the issuer has to be trusted. By default, no issuers are trusted. A list of untrusted issuers can be obtained from the node through:
GET <internal-node-address>/internal/vcr/v2/verifier/NutsOrganizationCredential/untrusted
This will return a list of all DIDs that are currently not trusted. If a DID is to be trusted should be validated out-of-band, eg: by phone or video conference call. The registered contact information for that DID could help in contacting the right party. Be aware that the provided contact information isn’t verified. So instead of asking: “is this your DID?”, ask: “could you please tell me your DID?”. After a DID has been verified, it can be trusted by calling the following API:
POST <internal-node-address>/internal/vcr/v2/verifier/trust
{
"issuer": "<did>",
"credentialType": "NutsOrganizationCredential"
}
Where <did>
must be replaced with the validated DID.
It’s also possible to update the vcr/trusted_issuers.yaml
file located in the data directory (configured via the datadir
property).
After a vendor has been trusted, any of its registered organizations should be searchable by name.
Note
Future development will see new cryptographic means. These means could enable the organization to self-register its name. The network should then migrate to a trust model where the issuer of those means is trusted instead of the different vendors.
Enabling a bolt
Organizations can be found on the network and endpoints have been defined. Now it’s time to enable specific bolts so users can start using data from other organizations. Every bolt requires its own configuration. This configuration is known as a Compound Service on the organization’s DID document. A Compound Service defines certain endpoint types and which endpoint to use for that type.
A Compound Service can be added with the following request:
POST <internal-node-address>/internal/didman/v1/did/<did>/compoundservice
{
"type": "<type>",
"serviceEndpoint": {
"<X>": "<endpoint_did>/serviceEndpoint?type=<Y>",
...
}
}
The parameters must be replaced:
<did>
must be replaced with the organization DID.<type>
must be replaced with the type defined by the bolt specification.<endpoint_did>
must be replaced with the vendor DID that defines the endpoints.<X>
must be replaced with the type required by the bolt specification. All types defined by the specification must be added, unless stated otherwise.<Y>
must be replaced with the correct endpoint type from the vendor DID Document.<endpoint_did>/serviceEndpoint?type=<Y>
must be a valid query within the corresponding DID Document.
For example, the eOverdracht sender requires an eOverdracht-sender
Compound Service with two endpoints: an oauth
endpoint and a fhir
endpoint.
The example can be added by the following request:
POST <internal-node-address>/internal/didman/v1/did/did:nuts:organization_identifier/compoundservice
{
"type": "eOverdracht-sender",
"serviceEndpoint": {
"oauth": "did:nuts:vendor_identifier/serviceEndpoint?type=production-oauth",
"fhir": "did:nuts:vendor_identifier/serviceEndpoint?type=eOverdracht-sender-fhir"
}
}
Note
As specified by RFC006, the type
MUST be unique within a DID Document.
Signing a contract with IRMA
This getting started manual shows how to successfully use IRMA to sign a contract. Contracts are used within the Nuts ecosystem to identify a user to other network participants. It also relates a user to the care organization that user is currently working for. The signed contract is used as token to authenticate the user’s (local) EHR identity to other nodes in the network and can be used as session token on the EHR. The contract is required for every request that results in personal and/or medical data being retrieved.
Basic requirements
To use IRMA as a means for signing a contract, the following is required:
the user has the IRMA app installed on an Android or iOS device with camera and an internet connection.
the user has retrieved the BRP and email credentials in the IRMA app.
the user interacts with the XIS/ECD via a recent browser capable of running javascript.
the vendor has a Nuts node running.
IRMA flow
We use the Nuts node as IRMA server and as tool to start an IRMA session. This follow the flow as described on this IRMA Github page. The XIS/ECD will have to provide two endpoints for the frontend. One endpoint to start a session and one to get the session result. More info on these endpoints will be provided further down.
Configuring the Nuts node
In the contract signing flow, the device running the IRMA app communicates with the Nuts node directly.
Therefore the Nuts node needs to be accessible to the public internet.
All APIs on the Nuts node starting with /public
(without a trailing slash) must be accessible over HTTPS without any additional security measures that could prevent access by mobile devices.
A domain must also be available which resolves to those APIs.
The domain must be configured on the Nuts node:
auth:
publicurl: https://example.com
The Nuts APIs used for signing will embed this URL in the QR code shown to the user. The javascript in the frontend will also use this URL (exposed via the QR code) to check the status of the signing session. Therefore the domain which serves the frontend must be able to do requests to that domain. The browser will require CORS headers to be configured on the domain configured in the Nuts node config. This can be done by the following snippet:
http:
default:
cors:
origin: "other.com"
Where other.com is the domain serving the frontend. For development purposes *
is also allowed.
If the public APIs are mounted on a different port/interface in the nuts config then the default
key should be changed to public
in the example above.
Setting up the frontend
For the frontend we’ll be using the irma-frontend-packages javascript library.
More info on how to use this library can be found on https://irma.app/docs/irma-frontend/.
You can choose to load the IRMA frontend packages javascript via an HTML tag, in which case you’ll need to build the javascript file yourself given the instructions on https://github.com/privacybydesign/irma-frontend-packages or you can choose to use npm
:
"dependencies": {
"@privacybydesign/irma-frontend": "^0.3.3"
}
Make sure you use the latest version.
IRMA allows for multiple frontends to be used. The most important ones are the web and popup frontends. The web frontend allows for embedding the IRMA web component within a html element. The popup frontend will render a new component that will render on top of the rest of the website. This manual will use the popup frontend.
A complete example:
let options = {
// Developer options
debugging: true,
// Front-end options
language: 'en',
// customize textual components
translations: {
header: "Sign your contract"
},
// Back-end options
session: {
// Point to your web backend
url: '/web/auth',
// The request that will be send to the backend:
start: {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.some_data)
},
// required to translate Nuts specific return values
mapping: {
sessionPtr: r => r.sessionPtr.clientPtr,
sessionToken: r => r.sessionID
}
}
};
// we'll use the popup frontend
let irmaPopup = irma.newPopup(options);
// start the interaction
irmaPopup.start()
.then(result => {
console.log("success!")
console.log(response)
})
.catch(error => {
if (error === 'Aborted') {
console.log('Aborted');
return;
}
console.error("error", error);
})
.finally(() => irmaPopup = irma.newPopup(options));
}
Lets break this down into parts.
// Developer options
debugging: true,
Is used to enabling debugging. The IRMA library will output more information helpful for development.
// Front-end options
language: 'en',
// customize textual components
translations: {
header: "Sign your contract"
},
Sets the language to english which will set some default textual representations on the IRMA web component.
The translations
configuration option can be used to change each of the textual representation on the IRMA web component.
In this case, only the header is changed.
// Back-end options
session: {
// Point to your web backend
url: '/web/auth',
// The request that will be send to the backend:
start: {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(this.some_data)
},
// required to translate Nuts specific return values
mapping: {
sessionPtr: r => r.sessionPtr.clientPtr,
sessionToken: r => r.sessionID
}
}
The session
object contains all the technical parts to connect the IRMA javascript library to your backend.
The contents of the start
object configures the initial request to start a signing session. You can control the type of request and the contents.
In this case, some data from the frontend is sent as JSON. This is optional and no particular data is required.
The url
, in this case /web/auth
, must be set so the frontend can access the following URLs:
<url>/session
<url>/session/<sessionToken>/result
These URLs must both be available on the backend. For the example above this means that both /web/auth/session/
and /web/auth/session/<sessionToken>/result
are available. The <sessionToken>
is the token that will be returned by the call to <url>/session/
.
How to parse the result of that call and extract the token is done via the mapping
object.
The mapping
object is a map where two keys are expected: sessionPtr
and sessionToken
.
sessionPtr
must point to the data that is used to render the QR code.
sessionToken
must point to the session token used to get the result.
Setting up the backend
As discussed in the previous chapter, the backend is required to expose two APIs to the frontend:
<url>/session
<url>/session/<sessionToken>/result
No particular security context is required, you may require a user session if needed.
Starting a session
The <url>/session
API is used to start a session.
To start a session at the Nuts node, a valid contract has to be drawn up first.
You can create such a contract with the following API on the Nuts node:
PUT /internal/auth/v1/contract/drawup
With the following body:
{
"type": "BehandelaarLogin",
"language": "NL",
"version": "v3",
"legalEntity": "did:nuts:90348275fjasihnva4857qp39hn",
"validFrom": "2006-01-02T15:04:05+02:00",
"validDuration": "2h"
}
The type
must be one of the valid Nuts contract types, currently only BehandelaarLogin
for Dutch and PractitionerLogin
for English are supported.
The language` selects the correct language, NL
for Dutch and EN
for english. The version
must be v3
.
The legalEntity
must refer to the DID of the current organization. The user either selects an organization to login for, or is already logged in.
The organization must have a DID as described in Getting Started on customer integration.
validFrom
is a RFC3339 compliant time string. validDuration
describes how long the contract is valid for. Time unit strings are used like 1h
or 60m
, the valid time units are: “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”. The local system timezone is used to format the date and time string.
The return value looks like:
{
"type": "PractitionerLogin",
"language": "EN",
"version": "v3",
"message": "EN:PractitionerLogin:v3 I hereby declare to act on behalf of CareBears located in CareTown. This declaration is valid from Monday, 2 January 2006 15:04:05 until Monday, 2 January 2006 17:04:05."
}
The message
from this result is used in the next part.
Start an IRMA session by calling the following API on the Nuts node:
POST /internal/auth/v1/signature/session
The body for this call looks like:
{
"means": "irma",
"payload": "<message>"
}
Where message
is the result from the contract call.
The result from this call must be passed directly to the frontend.
If any transformation is done, the mapping
setting in the frontend must be changed accordingly.
Getting the session result
The IRMA javascript frontend library will check for the status of the signing session. When the session has been completed it’ll call the following url:
GET <url>/session/<sessionToken>/result
where <url>
is the base url configured under session.url
in the javascript options and <sessionToken>
is the token returned by the previous call.
The backend must implement this API, the implementation must call the following API on the Nuts node:
GET /internal/auth/v1/signature/session/<sessionToken>
Any error in calling this service need to be relayed to the frontend. This will instruct the user on why things went wrong and what to do next. The call to the Nuts node will return the following response:
{
"status": "completed",
"verifiablePresentation": {
// ...
}
}
The status
field has a different content when a different signing means is used.
The presence of the verifiablePresentation
in the result is the main method of checking if the signing session succeeded.
verifiablePresentation
is the cryptographic proof that needs to be stored in the user session.
It’s required in the OAuth flow for obtaining an access token.
The backend should check if the signed contract (verifiable presentation) is still valid when using it.
The validity can be checked by calling the following API with the verifiable presentation at the place of <vp>
:
PUT /internal/auth/v1/signature/verify
with
{
"checkTime": "2006-01-02T15:54:05+02:00",
"verifiablePresentation": "<vp>"
}
It will return a structure similar to:
{
"validity": true,
"vpType": "NutsIrmaPresentation",
"issuerAttributes": {
"pbdf.gemeente.personalData.initials": "T",
"pbdf.gemeente.personalData.prefix": "",
"pbdf.gemeente.personalData.familyname": "Tester",
"pbdf.sidn-pbdf.email.email": "tester@example.com"
},
"credentials": {
"organization": "CareBears",
"validFrom": "2006-01-02T15:04:05+02:00",
"validTo": "2006-01-02T17:04:05+02:00"
}
}
The validity
will indicate its validity. An expired contract is considered invalid.
Getting Started on EHR integration
This getting started manual assumes the vendor and its clients (care organizations) are set up on the Nuts Network through Getting Started on customer integration. The next step is to integrate the vendor’s electronic health record (EHR) with the Nuts node to execute Bolts.
All APIs used in the following chapters are documented at the API page. There you will also find the OpenAPI specifications for generating client code.
Resolving Bolt endpoints
Bolts define which technical endpoints should be defined for exchanging information. These endpoints are grouped as services which are generally named after the Bolt they support. The Nuts registry (as described by Getting Started on customer integration) can be queried to find care organizations that support a particular Bolt, and to resolve the technical endpoints associated with it.
Searching organizations
To find care organizations (registered in the Nuts registry) that support a specific Bolt, the search organization API can be used. It takes a query parameter that’s used to match organization names and optionally a service type (from its DID document). If the DID service type is supplied the API only returns organizations which DID Document has a service with that type.
For example, the following API call searches the Nuts registry for organizations which name matches “Ziekenhuis” and have a service of type “secure-direct-messaging” on their DID Document:
GET <internal-node-address>/internal/didman/v1/search/organizations?query=Ziekenhuis&didServiceType=secure-direct-messaging
Note
The example DID service type “secure-direct-messaging” could be defined by a (fictional) “Secure Direct Messaging” Bolt to be published by organizations that allow their employees to securely chat with other organizations through Nuts.
The API call returns a list of search results where each entry contains the organization and its last DID Document:
[
{
"didDocument": {
"@context": "https://www.w3.org/ns/did/v1",
"assertionMethod": [
"did:nuts:JCx4c3ufdKNgaZJ4h54AghY8ZgCznptNpjHUtzvVgcvW#Cv0c4hlz4My7pKa6Wh6UN7gnTAXi5WUpNChqsUuIL1A"
],
"controller": "did:nuts:5bSHwHtpSZfSCdCqaHvzDceEkjgNuKvTWVvQPB5DdeD9",
"id": "did:nuts:JCx4c3ufdKNgaZJ4h54AghY8ZgCznptNpjHUtzvVgcvW",
"verificationMethod": [
/* etc */
],
"service": {
"type": "secure-direct-messaging",
/* etc */
}
},
"organization": {
"city": "Doornenburg",
"name": "Fort Pannerden"
}
}
]
For an organization to be returned as search results the following requirements must be met:
It must have an active DID Document.
The issuer of its verifiable credential (
NutsOrganizationCredential
) must be trusted by the local node.Its verifiable credential must not be expired or revoked.
The query
parameter is used to phonetically match the organization name: it supports partial matches and matches that sound like the given query.
Resolving endpoints
When an organization has been selected, the next step is to resolve the technical endpoints.
This is done by taking the compound service as specified by the Bolt and resolving its endpoint references to an actual URL endpoints.
You can use the DIDMan getCompoundServiceEndpoint
API operation for this.
Nuts Node APIs
Below you can discover the Nuts Node APIs and download their OpenAPI specifications:
Issuing and searching Verifiable Credentials
Issuing VCs
As a node, you can issue credentials with each DID you control (whether they are trusted is a different story). A credential is issued through the API or CLI. The node will add sensible defaults for:
@context
id
issuanceDate
proof
You are required to provide the credentialSubject, the issuer, the type and an optional expirationDate. So calling /internal/vcr/v2/issuer/vc with
{
"issuer": "did:nuts:ByJvBu2Ex21tNdn5s8FBnqmRBTCGkqRHms5ci7gKM8rg",
"type": "NutsOrganizationCredential",
"credentialSubject": {
"id": "did:nuts:9UKf9F9sRtiq4gR3bxfGQAeARtJeU8jvPqfWJcFP6ziN",
"organization": {
"name": "Because we care B.V.",
"city": "IJbergen"
}
},
"visibility": "public"
}
Will be expanded by the node to:
{
"context": [
"https://www.w3.org/2018/credentials/v1",
"https://nuts.nl/credentials/v1"
],
"credentialSubject": {
"id": "did:nuts:9UKf9F9sRtiq4gR3bxfGQAeARtJeU8jvPqfWJcFP6ziN",
"organization": {
"city": "IJbergen",
"name": "Because we care B.V."
}
},
"id": "did:nuts:ByJvBu2Ex21tNdn5s8FBnqmRBTCGkqRHms5ci7gKM8rg#a1d8ee3f-f404-44d5-bd07-71d3b144ce54",
"issuanceDate": "2021-03-05T09:37:05.732811+01:00",
"issuer": "did:nuts:ByJvBu2Ex21tNdn5s8FBnqmRBTCGkqRHms5ci7gKM8rg",
"proof": {
"created": "2021-03-05T09:37:05.732811+01:00",
"jws": "eyJhbGciOiJFUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..s6lxJa7pOpqlhcWhJoKRMJIJiD4i+IUkfmhy+rUvNzZayVHAq+lZaFxBsv9rQCe0ewpZq/6z3hSUOURo6mnHhg==",
"proofPurpose": "assertionMethod",
"type": "JsonWebSignature2020",
"verificationMethod": "did:nuts:ByJvBu2Ex21tNdn5s8FBnqmRBTCGkqRHms5ci7gKM8rg#gSEtbS2dOsS9PSrV13RwaZHz3Ps6OTI14GvLx8dPqgQ"
},
"type": [
"NutsOrganizationCredential",
"VerifiableCredential"
]
}
The visibility property indicates the contents of the VC are published on the network, so it can be read by everyone.
Searching VCs
You can search for VCs by providing a VC which should be used for matching in JSON-LD format. Searching works by posting a Verifiable Credential to /internal/vcr/v2/search that contains fields to match. The operation yields an array containing the matched verifiable credentials.
The example below searches for a NutsOrganizationCredential (note that the query field contains the credential):
{
"query": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://nuts.nl/credentials/v1"
],
"type": ["VerifiableCredential" ,"NutsOrganizationCredential"],
"credentialSubject": {
"id": "did:nuts:SKUYYi2g88ohjhiu49Q13ZWGXvp678sjNiM7UHUCMyw",
"organization": {
"name": "Because we care B.V.",
"city": "IJbergen"
}
}
}
}
Note the fields @context and type, these are required for making it a valid VC in JSON-LD. In the example above they also contain Nuts specific contexts and types (since we’re searching for a Nuts VC). The fields @context and type are not used as query parameters for searching, they are required to determine the right context. The following query does not return all NutsOrganizationCredential but all credentials.
{
"query": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://nuts.nl/credentials/v1"
],
"type": ["VerifiableCredential" ,"NutsOrganizationCredential"],
}
}
To find certain credentials, you’ll need to add fields that are required to exist in the desired credential. The following query searches for credentials that have a organization name that starts with an empty string. Any credential that does not have an organization name will be ignored. By default, field selection is done by matching the given value as prefix.
{
"query": {
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://nuts.nl/credentials/v1"
],
"type": ["VerifiableCredential" ,"NutsOrganizationCredential"],
"credentialSubject": {
"organization": {
"name": ""
}
}
}
}
By default only VCs from trusted issuers are returned. You can specify the searchOptions field to include VCs from untrusted issuers.
Frequently Encountered Errors
This section lists commonly seen errors, what they mean and what to do about it.
Error: connection closed before server preface received
When connecting to a remote node, the following error can occur:
Couldn’t connect to peer, reconnecting in XYZ seconds (peer=some.domain.nl:5555,err=unable to connect: context deadline exceeded: connection closed before server preface received)
This indicates the server is using TLS, but the local node is trying to connect without TLS. Check the network.tls.enabled setting.
Error: JWT signing key not present on this node
When inspecting an access token, the following error can occur:
Error while inspecting access token (error: JWT signing key not present on this node)
This indicates the JWT token you send to the Nuts node can’t be introspected by the Nuts node, because it can’t find the private key that was used to create the token. You probably:
# Try to introspect a token that your node didn’t create: only node that issued the token can introspect it. Or: # Your node’s private key storage is corrupt and your node lost access to its private keys (less probable).
Release notes
What has been changed, and how to update between versions.
Coconut update (v4.3.1)
Release date: 2022-11-28
This patch release fixes the following:
Synchronize calls to DIDMan to avoid parallel calls from clients creating conflicted DID documents
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v4.3.0…v4.3.1
Chestnut update (v4.3.0)
Release date: 2022-10-27
This update adds forward compatibility with the upcoming v5 release.
It removes validation of legalBase
from NutsAuthorizationCredential
, which was never properly defined in the JSON-LD contexts.
The upcoming v5 release will refuse to issue credentials with fields that were not defined in the credential’s context.
But, since legalBase
is required up until v4.3.0, it would mean future NutsAuthorizationCredentials
issued by upcoming v5 can’t be used in v4.
Hence, the removal of the validation, to become forwards compatible with v5.
See https://github.com/nuts-foundation/nuts-node/issues/1580 for more information
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v4.2.4…v4.3.0
Chestnut update (v4.2.4)
Release date: 2022-09-29
Set IRMA to production mode when the Nuts node is in strict-mode. This allows an IRMA app in non-developers-mode to connect to the Nuts node.
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v4.2.3…v4.2.4
Chestnut update (v4.2.3)
Release date: 2022-09-21
Bugfix for Hashicorp Vault key store backend: stacktrace on missing key
Bugfix VAULT_TOKEN gets overwritten with empty default
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v4.2.2…v4.2.3
Chestnut update (v4.2.2)
Release date: 2022-08-31
Bugfix for Redis: not being able to load state data from database.
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v4.2.0…v4.2.1
Chestnut update (v4.2.0)
Release date: 2022-08-29
Backports upstream features for connecting to Redis over TLS.
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v4.1.1…v4.2.0
Chestnut update (v4.1.1)
Release date: 2022-08-18
This patch adds TLS offloading for gRPC connections with support for DER encoded client certificates. This is required for supporting TLS offloading on HAProxy.
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v4.1.0…v4.1.1
Chestnut update (v4.1.0)
Release date: 2022-08-04
This minor release adds TLS offloading for gRPC connections. The TLS Configuration page contains instructions on how to setup various TLS deployment schemes.
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v4.0.0…v4.1.0
Chestnut (v4.0.0)
Release date: 2022-07-22
This release introduces a pluggable storage system and support for:
BBolt backups
Experimental Redis support
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v3.0.0…v4.0.0
Cashew (v3.0.0)
Release date: 2022-06-01
This release no longer contains the V1 network protocol.
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v2.0.0…v3.0.0
Brazil (v2.0.0)
Release date: 2022-04-29
This version implements the V2 network protocol. The V2 network protocol combines gossip style messages with a fast reconciliation protocol for larger difference sets. The protocol can quickly identify hundreds of missing transactions. The new protocol is much faster than the old protocol and its performance is currently limited by the database performance.
Besides the improved network protocol, this version also implements semantic searching for Verifiable Credentials. Till this version, searching for VCs only supported the NutsOrganizationCredential and NutsAuthorizationCredential. With the new semantic search capabilities all kinds of credentials can be issued and found. This is the first step for the Nuts node to become a toolbox that supports multiple domains.
Full Changelog: https://github.com/nuts-foundation/nuts-node/compare/v1.0.0…v2.0.0
Almond (v1.0.0)
Release date: 2022-04-01
This is the initial release of the Nuts node reference implementation. It implements RFC001 - RFC016 specified by the Nuts specification. This release is intended for developers. It contains a stable API that will be backwards compatible for the next versions. The releases until the first production release will mainly focus on network and Ops related features.
To start using this release, please consult the getting started section.
Features / improvements
Future releases will list new features and improvements that have been added since the previous release.
Dropped features
New major releases might drop support for features that have been deprecated in a previous release. Keep an eye on this section for every release.
Deprecated features
Some features will be deprecated because they have been succeeded by an improved version or when they are no longer used. Removing old code helps in reducing maintenance costs of the code base. Features that are marked as deprecated will be listed here. Any vendor using these features will have until next version to migrate to the alternative. Keep an eye on this section for every release.
VCR V1 API is deprecated and will be removed in the next release. Please migrate all calls to the V2 API.
Bugfixes
This section contains a list of bugfixes. It’ll match resolved Github issues with the bug tag.
Roadmap

Recommended Deployment
This document aims to describe the systems and their components involved in deploying a Nuts node in a production environment. The target audience are engineers that want to deploy a Nuts Node in their environment for a Service Provider (SP).
It does not detail services and interfaces specified by Bolts: those should be documented by the particular Bolts and should be regarded as extensions to the deployment described here.
The container diagram documents the recommended way of deploying a Nuts node using the features supported by the Nuts node’s version.
The diagrams are in C4 model notation.
System Landscape
The diagram below depicts the users and systems that interact with the Nuts node of the local SP.
Containers
This section details the system with involved containers (which can be native or containerized processes, physical or remote database servers or even networked filesystems). It lists the interfaces of the Nuts node, who uses them and how they should be secured.
Note
There’s a number of upcoming features that will change the recommended deployment, notably:
SSL/TLS termination for gRPC traffic on a reverse proxy
Redis database support
Clustering support
Nuts Node
Server that implements the Nuts specification that connects to the Nuts network. It will usually run as Docker container or Kubernetes pod.
Interfaces/Endpoints
HTTP /internal: for managing everything related to DIDs, VCs and the Nuts Node itself. Very sensitive endpoints with no additional built-in security, so care should be taken that no unauthorized parties can access it. Since it binds to the shared HTTP interface by default (port
1323
), it is recommended to bind it to an alternative interface to securer routing.Users: operators, SPs administrative and EHR applications.
Security: restrict access through network separation and platform authentication.
HTTP /public: for accessing public services, e.g. IRMA authentication.
Users: IRMA app.
Security: HTTPS with server certificate (on proxy). Monitor traffic to detect attacks.
HTTP /n2n: for providing Nuts services to other nodes (e.g. creating access tokens). The local node also calls other nodes on their /n2n endpoint, these outgoing calls are subject to the same security requirements.
Users: Nuts nodes of other SPs.
Security: HTTPS with server- and client certificates (mTLS) according to network trust anchors (on proxy). Monitor traffic to detect attacks.
gRPC: for communicating with other Nuts nodes according to the network protocol. Uses HTTP/2 as transport, both outbound and inbound.
Users: Nuts nodes of other SPs.
Security: HTTPS with server- and client certificates (mTLS) according to network trust anchors. This is provided by the Nuts node.
HTTP /status: for inspecting the health of the server, returns
OK
if healthy.Users: monitoring tooling.
Security: Not strictly required, but advised to restrict access.
HTTP /status/diagnostics: for inspecting diagnostic information of the server.
Users: monitoring tooling, system administrators.
Security: Not strictly required, but advised to restrict access.
HTTP /metrics: for scraping metrics in Prometheus format.
Users: monitoring/metrics tooling.
Security: Not strictly required, but advised to restrict access.
stdout: the server logs to standard out, which can be configured to output in JSON format for integration with existing log tooling.
Subdomains
There are several endpoints that need to be accessed by external systems. You typically configure 2 subdomains for these, given example.com and the acceptance environment:
nuts-acc.example.com for traffic between nodes: * HTTP traffic to /n2n * gRPC traffic. gRPC will have to be bound on a separate port, e.g. 5555 (default).
nuts-public-acc.example.com for HTTP traffic to /public
These exact subdomain names are by no means required and can be adjusted to your organization’s requirements.
Reverse Proxy
Process that protects and routes HTTP access (specified above) to the Nuts Node. Typically a standalone HTTP proxy that resides in a DMZ and/or an ingress service on a cloud platform. It will act as SSL/TLS terminator, with only a server certificate or requiring a client certificate as well (depending on the endpoint).
The Nuts Node looks for a header called X-Forwarded-For to determine the client IP when logging HTTP calls. Refer to the documentation of your proxy on how to set this header.
Nuts Node Client
CLI application used by system administrators to manage the Nuts Node and the SPs presence on the network, which calls the REST API of the Nuts Node.
It is included in the Nuts Node server, so it can be executed in the Docker container (using docker exec
) or standalone process.
Database
BBolt database where the Nuts Node stores its data. The database is on disk (by default in /opt/nuts/data
) so make sure the data is retained, especially in a cloud environment.
It is recommended to backup the database using the provided backup feature (see config options of the storage engine).
Private Key Storage
Creating DID documents causes private keys to be generated, which need to be safely stored so the Nuts node can access them. It is recommended to store them in Vault. Refer to the config options of the crypto engine and Vault documentation for configuring it.
Configuring the Nuts Node
The Nuts node can be configured using a YAML configuration file, environment variables and commandline params.
The parameters follow the following convention:
$ nuts --parameter X
is equal to $ NUTS_PARAMETER=X nuts
is equal to parameter: X
in a yaml file.
Or for this piece of yaml
nested:
parameter: X
is equal to $ nuts --nested.parameter X
is equal to $ NUTS_NESTED_PARAMETER=X nuts
Config parameters for engines are prepended by the engine.ConfigKey
by default (configurable):
engine:
nested:
parameter: X
is equal to $ nuts --engine.nested.parameter X
is equal to $ NUTS_ENGINE_NESTED_PARAMETER=X nuts
While most options are a single value, some are represented as a list (indicated with the square brackets in the table below).
To provide multiple values through flags or environment variables you can separate them with a comma (,
).
Ordering
Command line parameters have the highest priority, then environment variables, then parameters from the configfile and lastly defaults.
The location of the configfile is determined by the environment variable NUTS_CONFIGFILE
or the commandline parameter --configfile
. If both are missing the default location ./nuts.yaml
is used.
Server options
The following options can be configured on the server:
Key |
Default |
Description |
---|---|---|
configfile |
nuts.yaml |
Nuts config file |
cpuprofile |
When set, a CPU profile is written to the given path. Ignored when strictmode is set. |
|
datadir |
./data |
Directory where the node stores its files. |
internalratelimiter |
true |
When set, expensive internal calls are rate-limited to protect the network. Always enabled in strict mode. |
loggerformat |
text |
Log format (text, json) |
strictmode |
false |
When set, insecure settings are forbidden. |
verbosity |
info |
Log level (trace, debug, info, warn, error) |
http.default.address |
:1323 |
Address and port the server will be listening to |
http.default.cors.origin |
[] |
When set, enables CORS from the specified origins for the on default HTTP interface. |
tls.certheader |
Name of the HTTP header that will contain the client certificate when TLS is offloaded. |
|
tls.offload |
Whether to enable TLS offloading for incoming connections. If enabled tls.certheader must be configured as well. |
|
Auth |
||
auth.clockskew |
5000 |
Allowed JWT Clock skew in milliseconds |
auth.contractvalidators |
[irma,uzi,dummy] |
sets the different contract validators to use |
auth.http.timeout |
30 |
HTTP timeout (in seconds) used by the Auth API HTTP client |
auth.irma.autoupdateschemas |
true |
set if you want automatically update the IRMA schemas every 60 minutes. |
auth.irma.schememanager |
pbdf |
IRMA schemeManager to use for attributes. Can be either ‘pbdf’ or ‘irma-demo’. |
auth.publicurl |
public URL which can be reached by a users IRMA client, this should include the scheme and domain: https://example.com. Additional paths should only be added if some sort of url-rewriting is done in a reverse-proxy. |
|
Crypto |
||
crypto.storage |
fs |
Storage to use, ‘fs’ for file system, vaultkv for Vault KV store, default: fs. |
crypto.vault.address |
The Vault address. If set it overwrites the VAULT_ADDR env var. |
|
crypto.vault.pathprefix |
kv |
The Vault path prefix. default: kv. |
crypto.vault.timeout |
5s |
Timeout of client calls to Vault, in Golang time.Duration string format (e.g. 5s). |
crypto.vault.token |
The Vault token. If set it overwrites the VAULT_TOKEN env var. |
|
Event manager |
||
events.nats.hostname |
localhost |
Hostname for the NATS server |
events.nats.port |
4222 |
Port where the NATS server listens on |
events.nats.storagedir |
Directory where file-backed streams are stored in the NATS server |
|
events.nats.timeout |
30 |
Timeout for NATS server operations |
JSONLD |
||
jsonld.contexts.localmapping |
This setting allows mapping external URLs to local files for e.g. preventing external dependencies. These mappings have precedence over those in remoteallowlist. |
|
jsonld.contexts.remoteallowlist |
In strict mode, fetching external JSON-LD contexts is not allowed except for context-URLs listed here. |
|
Network |
||
network.bootstrapnodes |
[] |
List of bootstrap nodes (<host>:<port>) which the node initially connect to. |
network.certfile |
PEM file containing the server certificate for the gRPC server. Required when network.enabletls is true. |
|
network.certkeyfile |
PEM file containing the private key of the server certificate. Required when network.enabletls is true. |
|
network.connectiontimeout |
5000 |
Timeout before an outbound connection attempt times out (in milliseconds). |
network.disablenodeauthentication |
false |
Disable node DID authentication using client certificate, causing all node DIDs to be accepted. Unsafe option, only intended for workshops/demo purposes. Not allowed in strict-mode. |
network.enablediscovery |
true |
Whether to enable automatic connecting to other nodes. |
network.enabletls |
true |
Whether to enable TLS for gRPC connections, which can be disabled for demo/development purposes. It is NOT meant for TLS offloading (see tls.offload). |
network.grpcaddr |
:5555 |
Local address for gRPC to listen on. If empty the gRPC server won’t be started and other nodes will not be able to connect to this node (outbound connections can still be made). |
network.maxbackoff |
24h0m0s |
Maximum between outbound connections attempts to unresponsive nodes (in Golang duration format, e.g. 1h, 30m). |
network.nodedid |
Specifies the DID of the organization that operates this node, typically a vendor for EPD software. It is used to identify the node on the network. If the DID document does not exist of is deactivated, the node will not start. |
|
network.protocols |
[] |
Specifies the list of network protocols to enable on the server. They are specified by version (1, 2). If not set, all protocols are enabled. |
network.truststorefile |
PEM file containing the trusted CA certificates for authenticating remote gRPC servers. |
|
network.v2.diagnosticsinterval |
5000 |
Interval (in milliseconds) that specifies how often the node should broadcast its diagnostic information to other nodes (specify 0 to disable). |
network.v2.gossipinterval |
5000 |
Interval (in milliseconds) that specifies how often the node should gossip its new hashes to other nodes. |
Storage |
||
storage.bbolt.backup.directory |
Target directory for BBolt database backups. |
|
storage.bbolt.backup.interval |
0s |
Interval, formatted as Golang duration (e.g. 10m, 1h) at which BBolt database backups will be performed. |
storage.redis.address |
Redis database server address. This can be a simple host:port or a Redis connection URL with scheme, auth and other options. |
|
storage.redis.database |
Redis database name, which is used as prefix every key. Can be used to have multiple instances use the same Redis instance. |
|
storage.redis.password |
Redis database password. If set, it overrides the username in the connection URL. |
|
storage.redis.tls.truststorefile |
PEM file containing the trusted CA certificate(s) for authenticating remote Redis servers. Can only be used when connecting over TLS (use ‘rediss://’ as scheme in address). |
|
storage.redis.username |
Redis database username. If set, it overrides the username in the connection URL. |
This table is automatically generated using the configuration flags in the core and engines. When they’re changed the options table must be regenerated using the Makefile:
$ make update-docs
Custom Credential Configuration
Introduction
The Nuts node by default is configured to handle a set of Nuts credentials, such as NutsAuthorizationCredential and NutsOrganizationCredential. These credentials are accepted and indexed automatically. If you want to use custom credentials for your use-case, you have to tell your Nuts node how they are structured. A Verifiable Credential is structured as a JSON-LD document. Adding extra fields to a JSON-LD document requires adding an extra @context-definition which describes these fields.
To configure your Nuts node to recognise these extra fields and custom types, you have to overwrite the JSON-LD configuration. This can be done using the jsonld.contexts config. More information about configuration options and its default values can be found at the config documentation.
The node can fetch a context from a http endpoint, but only if this location is explicitly listed as safe. For this you use the jsonld.contexts.remoteallowlist. Or it can fetch the context definition from disk, using a mapping from url to path relative to current directory, or just using absolute paths. For this use the jsonld.contexts.localmapping
Note
When configuring a list or map value, all values are replaced by your custom values. So if you want to just add an extra context and also use the Nuts context, make sure to add the default values to your config as well!
The default contexts can be accessed using the embedded file system assets/contexts. The contents of this directory can be found on Github: The default loaded contexts can be downloaded from the Github repo.
Example configuration
Example configuration with an allowed remote context and another locally mapped context:
jsonld:
contexts:
remoteallowlist:
- https://schema.org
- https://www.w3.org/2018/credentials/v1
- https://w3c-ccg.github.io/lds-jws2020/contexts/lds-jws2020-v1.json
- https://other-usecase-website.nl/use-case-context.ldjson
localmapping:
- https://nuts.nl/credentials/v1: "/abs/path/to/contexts/nuts.ldjson"
- https://yourdomain.nl/custom-context.ldjson: "/abs/path/to/contexts/custom-context.ldjson"
- https://default-context/v1.ldjson: "assets/contexts/lds-jws2020-v1.ldjson"
- https://relative-path-usage/v42/ldjson: "./data/vcr/contexts/v42.ldjson"
Fetching & Caching
During startup of the node, remote contexts are fetched and cached. If the contents of a remote context changes, the node must be restarted in order for these changes to have effect. Only remote context listed in the remoteallowlist are fetched. Local mappings can be used to pin a version of a context, so no unseen changes can be made. Working with local mappings is also useful for developing purposes when the remote context is older or non-existent. When you work with local mappings, make sure all nodes involved in the use-case have the same custom context configured.
Searching and indexing
Searching for custom credentials works just as Nuts provided credentials as described in Searching VCs. Note however that the extra fields in the credentialSubject added by the custom credential are not indexed by the credential store. Searching for these fields is notably slower (depending on the query and amount of custom credentials). If this becomes a problem, inform the Nuts development team so an appropriate solution can be found.
Resources
Introduction into JSON-LD: https://json-ld.org/
The default loaded context definitions: https://github.com/nuts-foundation/nuts-node/tree/master/vcr/assets/assets/contexts
Nuts node configuration options including the current default values: config documentation
Monitoring the Nuts Node
Basic service health
A status endpoint is provided to check if the service is running and if the web server has been started. The endpoint is available over http so it can be used by a wide range of health checking services. It does not provide any information on the individual engines running as part of the executable. The main goal of the service is to give a YES/NO answer for if the service is running?
GET /status
It’ll return an “OK” response and a 200 status code.
Basic diagnostics
GET /status/diagnostics
It’ll return some text displaying the current status of the various services:
Status
Registered engines: [Status Logging]
Logging
verbosity: INFO
If you supply application/json
for the Accept
HTTP header it will return the diagnostics in JSON format.
Metrics
The Nuts service executable has build-in support for Prometheus. Prometheus is a time-series database which supports a wide variety of services. It also allows for exporting metrics to different visualization solutions like Grafana. See https://prometheus.io/ for more information on how to run Prometheus. The metrics are exposed at /metrics
Configuration
In order for metrics to be gathered by Prometheus. A job
has to be added to the prometheus.yml
configuration file.
Below is a minimal configuration file that will only gather Nuts metrics:
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'nuts'
metrics_path: '/metrics'
scrape_interval: 5s
static_configs:
- targets: ['127.0.0.1:1323']
It’s important to enter the correct IP/domain and port where the Nuts node can be found!
Exported metrics
The Nuts service executable exports the following metric namespaces:
nuts_
contains metrics related to the functioning of the Nuts nodeprocess_
contains OS metrics related to the processgo_
contains Go metrics related to the processhttp_
contains metrics related to HTTP calls to the Nuts nodepromhttp_
contains metrics related to HTTP calls to the Nuts node’s/metrics
endpoint
Network DAG Visualization
All network transactions form a directed acyclic graph (DAG) which helps achieving consistency and data completeness. Since it’s a hard to debug, complex structure, the network API provides a visualization which can be queried from /internal/network/v1/diagnostics/graph. It is returned in the dot format which can then be rendered to an image using dot or graphviz (given you saved the output to input.dot):
dot -T png -o output.png input.dot
Using query parameters start and end it is possible to retrieve a range of transactions. /internal/network/v1/diagnostics/graph?start=10&end=12 will return a graph with all transactions containing Lamport Clock 10 and 11. Both parameters need to be non-negative integers, and start < end. If no value is provided, start=0 and end=inf. Querying a range can be useful if only a certain range is of interest, but may also be required to generate the graph using dot.
CPU profiling
It’s possible to enable CPU profiling by passing the --cpuprofile=/some/location.dmp
option.
This will write a CPU profile to the given location when the node shuts down.
The resulting file can be analyzed with Go tooling:
go tool pprof /some/location.dmp
The tooling includes a help function to get you started. To get started use the web
command inside the tooling.
It’ll open a SVG in a browser and give an overview of what the node was doing.
Administration using the CLI
The Nuts executable this project provides can be used to both run a Nuts server (a.k.a. node) and administer a running node remotely. This chapter explains how to administer your running Nuts node.
Prerequisites
The following is needed to run a Nuts node:
Nuts executable for your platform, or a Nuts docker container.
The address of your running Nuts node. You can pass this using the address variable.
Commands
Run the executable without command or flags, or with the help command to find out what commands are supported:
nuts
For example, to list all network transactions in your node (replace the value of NUTS_ADDRESS with the HTTP address of your Nuts node):
NUTS_ADDRESS=my-node:1323 nuts network list
You can also use the Nuts docker image to run a command (against a remote Nuts node):
docker run nutsfoundation/nuts-node --address=http://my-node:1323 network list
Or inside a running Nuts docker container (against the running Nuts node):
docker exec nuts-container-name nuts network list
See CLI Command Reference for the available commands.
The following options can be supplied when running CLI commands:
Key |
Default |
Description |
---|---|---|
address |
localhost:1323 |
Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it ‘http://’ is prepended. |
timeout |
10s |
Client time-out when performing remote operations, such as ‘500ms’ or ’10s’. Refer to Golang’s ‘time.Duration’ syntax for a more elaborate description of the syntax. |
verbosity |
info |
Log level (trace, debug, info, warn, error) |
Backup & restore procedures
The Nuts node supports different backends for storing data. This page describes the backup and restore procedures per backend. A restore procedure may contain additional steps to take besides restoring the data from backup.
Private keys
The private keys are the most important of all data. When lost, all data has to be recreated which might include asking customer to resign certain documents. The Nuts node provides two ways of storing private keys: local filesystem and via Hashicorp Vault. Vault is the recommended store for storing private keys in a production environment. Please consult the Vault documentation on how to manage your backups.
BBolt
The default storage for a Nuts node is BBolt. BBolt is a key-value store that stores data on disk. A BBolt store can only be accessed by a single process. Private keys are not stored in BBolt and have their own backup/restore procedure.
Backup
By default, the BBolt store isn’t backed up. To enable backups add these configuration options:
storage:
databases:
bbolt:
backup:
directory: /opt/nuts/shelf
interval: 1h
The directory
must point to a local or mounted directory.
The interval
must be formatted as a number and time unit. Valid time units are s
(seconds), m
(minutes), h
(hours).
The Nuts node will place backups at the set interval in the configured directory. It’ll create sub-directories for different components.
The file names are the same as in the node’s datadir
.
The backup process will write to a temporary file first and when done rename that file.
The backup process will only keep a single file per store.
If you want to keep hourly, daily and weekly backups, you’ll have to solve this by using tools like rsync
and rsnapshot
(or others).
Restore
To restore a backup, follow the following steps:
shutdown the node.
remove the following directories from the
datadir
:events
,network
,vcr
andvdr
copy
network/data.db
,vcr/issued-credentials-backup.db
andvdr/didstore.db
from your backup to thedatadir
(keep directory structure).start your node
make an empty POST call to
/internal/network/v1/reprocess?type=application/vc+json
make an empty POST call to
/internal/network/v1/reprocess?type=application/ld+json;type=revocation
When making the API calls, make sure you use the proper URL escaping. Reprocess calls return immediately and will do the work in the background.
CLI Command Reference
nuts
Nuts executable which can be used to run the Nuts server or administer the remote Nuts server.
nuts [flags]
-h, --help help for nuts
nuts config
Prints the current config
nuts config [flags]
--auth.clockskew int Allowed JWT Clock skew in milliseconds (default 5000)
--auth.contractvalidators strings sets the different contract validators to use (default [irma,uzi,dummy])
--auth.http.timeout int HTTP timeout (in seconds) used by the Auth API HTTP client (default 30)
--auth.irma.autoupdateschemas set if you want automatically update the IRMA schemas every 60 minutes. (default true)
--auth.irma.schememanager string IRMA schemeManager to use for attributes. Can be either 'pbdf' or 'irma-demo'. (default "pbdf")
--auth.publicurl string public URL which can be reached by a users IRMA client, this should include the scheme and domain: https://example.com. Additional paths should only be added if some sort of url-rewriting is done in a reverse-proxy.
--configfile string Nuts config file (default "nuts.yaml")
--cpuprofile string When set, a CPU profile is written to the given path. Ignored when strictmode is set.
--crypto.storage string Storage to use, 'fs' for file system, vaultkv for Vault KV store, default: fs. (default "fs")
--crypto.vault.address string The Vault address. If set it overwrites the VAULT_ADDR env var.
--crypto.vault.pathprefix string The Vault path prefix. default: kv. (default "kv")
--crypto.vault.timeout duration Timeout of client calls to Vault, in Golang time.Duration string format (e.g. 5s). (default 5s)
--crypto.vault.token string The Vault token. If set it overwrites the VAULT_TOKEN env var.
--datadir string Directory where the node stores its files. (default "./data")
--events.nats.hostname string Hostname for the NATS server (default "localhost")
--events.nats.port int Port where the NATS server listens on (default 4222)
--events.nats.storagedir string Directory where file-backed streams are stored in the NATS server
--events.nats.timeout int Timeout for NATS server operations (default 30)
-h, --help help for config
--http.default.address string Address and port the server will be listening to (default ":1323")
--http.default.cors.origin strings When set, enables CORS from the specified origins for the on default HTTP interface.
--internalratelimiter When set, expensive internal calls are rate-limited to protect the network. Always enabled in strict mode. (default true)
--jsonld.contexts.localmapping stringToString This setting allows mapping external URLs to local files for e.g. preventing external dependencies. These mappings have precedence over those in remoteallowlist. (default [https://nuts.nl/credentials/v1=assets/contexts/nuts.ldjson,https://www.w3.org/2018/credentials/v1=assets/contexts/w3c-credentials-v1.ldjson,https://w3c-ccg.github.io/lds-jws2020/contexts/lds-jws2020-v1.json=assets/contexts/lds-jws2020-v1.ldjson,https://schema.org=assets/contexts/schema-org-v13.ldjson])
--jsonld.contexts.remoteallowlist strings In strict mode, fetching external JSON-LD contexts is not allowed except for context-URLs listed here. (default [https://schema.org,https://www.w3.org/2018/credentials/v1,https://w3c-ccg.github.io/lds-jws2020/contexts/lds-jws2020-v1.json])
--loggerformat string Log format (text, json) (default "text")
--network.bootstrapnodes <host>:<port> List of bootstrap nodes (<host>:<port>) which the node initially connect to.
--network.certfile network.enabletls PEM file containing the server certificate for the gRPC server. Required when network.enabletls is `true`.
--network.certkeyfile network.enabletls PEM file containing the private key of the server certificate. Required when network.enabletls is `true`.
--network.connectiontimeout int Timeout before an outbound connection attempt times out (in milliseconds). (default 5000)
--network.disablenodeauthentication Disable node DID authentication using client certificate, causing all node DIDs to be accepted. Unsafe option, only intended for workshops/demo purposes. Not allowed in strict-mode.
--network.enablediscovery Whether to enable automatic connecting to other nodes. (default true)
--network.enabletls tls.offload Whether to enable TLS for gRPC connections, which can be disabled for demo/development purposes. It is NOT meant for TLS offloading (see tls.offload). (default true)
--network.grpcaddr string Local address for gRPC to listen on. If empty the gRPC server won't be started and other nodes will not be able to connect to this node (outbound connections can still be made). (default ":5555")
--network.maxbackoff 1h Maximum between outbound connections attempts to unresponsive nodes (in Golang duration format, e.g. 1h, `30m`). (default 24h0m0s)
--network.nodedid string Specifies the DID of the organization that operates this node, typically a vendor for EPD software. It is used to identify the node on the network. If the DID document does not exist of is deactivated, the node will not start.
--network.protocols ints Specifies the list of network protocols to enable on the server. They are specified by version (1, 2). If not set, all protocols are enabled.
--network.truststorefile string PEM file containing the trusted CA certificates for authenticating remote gRPC servers.
--network.v2.diagnosticsinterval int Interval (in milliseconds) that specifies how often the node should broadcast its diagnostic information to other nodes (specify 0 to disable). (default 5000)
--network.v2.gossipinterval int Interval (in milliseconds) that specifies how often the node should gossip its new hashes to other nodes. (default 5000)
--storage.bbolt.backup.directory string Target directory for BBolt database backups.
--storage.bbolt.backup.interval duration Interval, formatted as Golang duration (e.g. 10m, 1h) at which BBolt database backups will be performed.
--storage.redis.address host:port Redis database server address. This can be a simple host:port or a Redis connection URL with scheme, auth and other options.
--storage.redis.database string Redis database name, which is used as prefix every key. Can be used to have multiple instances use the same Redis instance.
--storage.redis.password string Redis database password. If set, it overrides the username in the connection URL.
--storage.redis.tls.truststorefile string PEM file containing the trusted CA certificate(s) for authenticating remote Redis servers. Can only be used when connecting over TLS (use 'rediss://' as scheme in address).
--storage.redis.username string Redis database username. If set, it overrides the username in the connection URL.
--strictmode When set, insecure settings are forbidden.
--tls.certheader string Name of the HTTP header that will contain the client certificate when TLS is offloaded.
--tls.offload tls.certheader Whether to enable TLS offloading for incoming connections. If enabled tls.certheader must be configured as well.
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts crypto fs2vault
Imports private keys from filesystem based storage into Vault. The given directory must contain the private key files.The Nuts node must be configured to use Vault as crypto storage. Can only be run on the local Nuts node, from the directory where nuts.yaml resides.
nuts crypto fs2vault [directory] [flags]
--auth.clockskew int Allowed JWT Clock skew in milliseconds (default 5000)
--auth.contractvalidators strings sets the different contract validators to use (default [irma,uzi,dummy])
--auth.http.timeout int HTTP timeout (in seconds) used by the Auth API HTTP client (default 30)
--auth.irma.autoupdateschemas set if you want automatically update the IRMA schemas every 60 minutes. (default true)
--auth.irma.schememanager string IRMA schemeManager to use for attributes. Can be either 'pbdf' or 'irma-demo'. (default "pbdf")
--auth.publicurl string public URL which can be reached by a users IRMA client, this should include the scheme and domain: https://example.com. Additional paths should only be added if some sort of url-rewriting is done in a reverse-proxy.
--configfile string Nuts config file (default "nuts.yaml")
--cpuprofile string When set, a CPU profile is written to the given path. Ignored when strictmode is set.
--crypto.storage string Storage to use, 'fs' for file system, vaultkv for Vault KV store, default: fs. (default "fs")
--crypto.vault.address string The Vault address. If set it overwrites the VAULT_ADDR env var.
--crypto.vault.pathprefix string The Vault path prefix. default: kv. (default "kv")
--crypto.vault.timeout duration Timeout of client calls to Vault, in Golang time.Duration string format (e.g. 5s). (default 5s)
--crypto.vault.token string The Vault token. If set it overwrites the VAULT_TOKEN env var.
--datadir string Directory where the node stores its files. (default "./data")
--events.nats.hostname string Hostname for the NATS server (default "localhost")
--events.nats.port int Port where the NATS server listens on (default 4222)
--events.nats.storagedir string Directory where file-backed streams are stored in the NATS server
--events.nats.timeout int Timeout for NATS server operations (default 30)
-h, --help help for fs2vault
--http.default.address string Address and port the server will be listening to (default ":1323")
--http.default.cors.origin strings When set, enables CORS from the specified origins for the on default HTTP interface.
--internalratelimiter When set, expensive internal calls are rate-limited to protect the network. Always enabled in strict mode. (default true)
--jsonld.contexts.localmapping stringToString This setting allows mapping external URLs to local files for e.g. preventing external dependencies. These mappings have precedence over those in remoteallowlist. (default [https://nuts.nl/credentials/v1=assets/contexts/nuts.ldjson,https://www.w3.org/2018/credentials/v1=assets/contexts/w3c-credentials-v1.ldjson,https://w3c-ccg.github.io/lds-jws2020/contexts/lds-jws2020-v1.json=assets/contexts/lds-jws2020-v1.ldjson,https://schema.org=assets/contexts/schema-org-v13.ldjson])
--jsonld.contexts.remoteallowlist strings In strict mode, fetching external JSON-LD contexts is not allowed except for context-URLs listed here. (default [https://schema.org,https://www.w3.org/2018/credentials/v1,https://w3c-ccg.github.io/lds-jws2020/contexts/lds-jws2020-v1.json])
--loggerformat string Log format (text, json) (default "text")
--network.bootstrapnodes <host>:<port> List of bootstrap nodes (<host>:<port>) which the node initially connect to.
--network.certfile network.enabletls PEM file containing the server certificate for the gRPC server. Required when network.enabletls is `true`.
--network.certkeyfile network.enabletls PEM file containing the private key of the server certificate. Required when network.enabletls is `true`.
--network.connectiontimeout int Timeout before an outbound connection attempt times out (in milliseconds). (default 5000)
--network.disablenodeauthentication Disable node DID authentication using client certificate, causing all node DIDs to be accepted. Unsafe option, only intended for workshops/demo purposes. Not allowed in strict-mode.
--network.enablediscovery Whether to enable automatic connecting to other nodes. (default true)
--network.enabletls tls.offload Whether to enable TLS for gRPC connections, which can be disabled for demo/development purposes. It is NOT meant for TLS offloading (see tls.offload). (default true)
--network.grpcaddr string Local address for gRPC to listen on. If empty the gRPC server won't be started and other nodes will not be able to connect to this node (outbound connections can still be made). (default ":5555")
--network.maxbackoff 1h Maximum between outbound connections attempts to unresponsive nodes (in Golang duration format, e.g. 1h, `30m`). (default 24h0m0s)
--network.nodedid string Specifies the DID of the organization that operates this node, typically a vendor for EPD software. It is used to identify the node on the network. If the DID document does not exist of is deactivated, the node will not start.
--network.protocols ints Specifies the list of network protocols to enable on the server. They are specified by version (1, 2). If not set, all protocols are enabled.
--network.truststorefile string PEM file containing the trusted CA certificates for authenticating remote gRPC servers.
--network.v2.diagnosticsinterval int Interval (in milliseconds) that specifies how often the node should broadcast its diagnostic information to other nodes (specify 0 to disable). (default 5000)
--network.v2.gossipinterval int Interval (in milliseconds) that specifies how often the node should gossip its new hashes to other nodes. (default 5000)
--storage.bbolt.backup.directory string Target directory for BBolt database backups.
--storage.bbolt.backup.interval duration Interval, formatted as Golang duration (e.g. 10m, 1h) at which BBolt database backups will be performed.
--storage.redis.address host:port Redis database server address. This can be a simple host:port or a Redis connection URL with scheme, auth and other options.
--storage.redis.database string Redis database name, which is used as prefix every key. Can be used to have multiple instances use the same Redis instance.
--storage.redis.password string Redis database password. If set, it overrides the username in the connection URL.
--storage.redis.tls.truststorefile string PEM file containing the trusted CA certificate(s) for authenticating remote Redis servers. Can only be used when connecting over TLS (use 'rediss://' as scheme in address).
--storage.redis.username string Redis database username. If set, it overrides the username in the connection URL.
--strictmode When set, insecure settings are forbidden.
--tls.certheader string Name of the HTTP header that will contain the client certificate when TLS is offloaded.
--tls.offload tls.certheader Whether to enable TLS offloading for incoming connections. If enabled tls.certheader must be configured as well.
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts didman svc add
Adds a service of the specified type to DID document identified by the given DID. The given service endpoint can either be a string a compound service map in JSON format.
nuts didman svc add [DID] [type] [endpoint] [flags]
-h, --help help for add
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts didman svc delete
Deletes a service from a DID document.
nuts didman svc delete [DID] [type] [flags]
-h, --help help for delete
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts network get
Gets a transaction from the network
nuts network get [ref] [flags]
-h, --help help for get
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts network list
Lists the transactions on the network
nuts network list [flags]
-h, --help help for list
--sort string sort the results on either time or type (default "time")
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts network payload
Retrieves the payload of a transaction from the network
nuts network payload [ref] [flags]
-h, --help help for payload
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts network peers
Get diagnostic information of the node’s peers
nuts network peers [flags]
-h, --help help for peers
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts network reprocess
Reprocess all transactions with the give contentType (ex: application/did+json)
nuts network reprocess [contentType] [flags]
-h, --help help for reprocess
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts server
Starts the Nuts server
nuts server [flags]
--auth.clockskew int Allowed JWT Clock skew in milliseconds (default 5000)
--auth.contractvalidators strings sets the different contract validators to use (default [irma,uzi,dummy])
--auth.http.timeout int HTTP timeout (in seconds) used by the Auth API HTTP client (default 30)
--auth.irma.autoupdateschemas set if you want automatically update the IRMA schemas every 60 minutes. (default true)
--auth.irma.schememanager string IRMA schemeManager to use for attributes. Can be either 'pbdf' or 'irma-demo'. (default "pbdf")
--auth.publicurl string public URL which can be reached by a users IRMA client, this should include the scheme and domain: https://example.com. Additional paths should only be added if some sort of url-rewriting is done in a reverse-proxy.
--configfile string Nuts config file (default "nuts.yaml")
--cpuprofile string When set, a CPU profile is written to the given path. Ignored when strictmode is set.
--crypto.storage string Storage to use, 'fs' for file system, vaultkv for Vault KV store, default: fs. (default "fs")
--crypto.vault.address string The Vault address. If set it overwrites the VAULT_ADDR env var.
--crypto.vault.pathprefix string The Vault path prefix. default: kv. (default "kv")
--crypto.vault.timeout duration Timeout of client calls to Vault, in Golang time.Duration string format (e.g. 5s). (default 5s)
--crypto.vault.token string The Vault token. If set it overwrites the VAULT_TOKEN env var.
--datadir string Directory where the node stores its files. (default "./data")
--events.nats.hostname string Hostname for the NATS server (default "localhost")
--events.nats.port int Port where the NATS server listens on (default 4222)
--events.nats.storagedir string Directory where file-backed streams are stored in the NATS server
--events.nats.timeout int Timeout for NATS server operations (default 30)
-h, --help help for server
--http.default.address string Address and port the server will be listening to (default ":1323")
--http.default.cors.origin strings When set, enables CORS from the specified origins for the on default HTTP interface.
--internalratelimiter When set, expensive internal calls are rate-limited to protect the network. Always enabled in strict mode. (default true)
--jsonld.contexts.localmapping stringToString This setting allows mapping external URLs to local files for e.g. preventing external dependencies. These mappings have precedence over those in remoteallowlist. (default [https://nuts.nl/credentials/v1=assets/contexts/nuts.ldjson,https://www.w3.org/2018/credentials/v1=assets/contexts/w3c-credentials-v1.ldjson,https://w3c-ccg.github.io/lds-jws2020/contexts/lds-jws2020-v1.json=assets/contexts/lds-jws2020-v1.ldjson,https://schema.org=assets/contexts/schema-org-v13.ldjson])
--jsonld.contexts.remoteallowlist strings In strict mode, fetching external JSON-LD contexts is not allowed except for context-URLs listed here. (default [https://schema.org,https://www.w3.org/2018/credentials/v1,https://w3c-ccg.github.io/lds-jws2020/contexts/lds-jws2020-v1.json])
--loggerformat string Log format (text, json) (default "text")
--network.bootstrapnodes <host>:<port> List of bootstrap nodes (<host>:<port>) which the node initially connect to.
--network.certfile network.enabletls PEM file containing the server certificate for the gRPC server. Required when network.enabletls is `true`.
--network.certkeyfile network.enabletls PEM file containing the private key of the server certificate. Required when network.enabletls is `true`.
--network.connectiontimeout int Timeout before an outbound connection attempt times out (in milliseconds). (default 5000)
--network.disablenodeauthentication Disable node DID authentication using client certificate, causing all node DIDs to be accepted. Unsafe option, only intended for workshops/demo purposes. Not allowed in strict-mode.
--network.enablediscovery Whether to enable automatic connecting to other nodes. (default true)
--network.enabletls tls.offload Whether to enable TLS for gRPC connections, which can be disabled for demo/development purposes. It is NOT meant for TLS offloading (see tls.offload). (default true)
--network.grpcaddr string Local address for gRPC to listen on. If empty the gRPC server won't be started and other nodes will not be able to connect to this node (outbound connections can still be made). (default ":5555")
--network.maxbackoff 1h Maximum between outbound connections attempts to unresponsive nodes (in Golang duration format, e.g. 1h, `30m`). (default 24h0m0s)
--network.nodedid string Specifies the DID of the organization that operates this node, typically a vendor for EPD software. It is used to identify the node on the network. If the DID document does not exist of is deactivated, the node will not start.
--network.protocols ints Specifies the list of network protocols to enable on the server. They are specified by version (1, 2). If not set, all protocols are enabled.
--network.truststorefile string PEM file containing the trusted CA certificates for authenticating remote gRPC servers.
--network.v2.diagnosticsinterval int Interval (in milliseconds) that specifies how often the node should broadcast its diagnostic information to other nodes (specify 0 to disable). (default 5000)
--network.v2.gossipinterval int Interval (in milliseconds) that specifies how often the node should gossip its new hashes to other nodes. (default 5000)
--storage.bbolt.backup.directory string Target directory for BBolt database backups.
--storage.bbolt.backup.interval duration Interval, formatted as Golang duration (e.g. 10m, 1h) at which BBolt database backups will be performed.
--storage.redis.address host:port Redis database server address. This can be a simple host:port or a Redis connection URL with scheme, auth and other options.
--storage.redis.database string Redis database name, which is used as prefix every key. Can be used to have multiple instances use the same Redis instance.
--storage.redis.password string Redis database password. If set, it overrides the username in the connection URL.
--storage.redis.tls.truststorefile string PEM file containing the trusted CA certificate(s) for authenticating remote Redis servers. Can only be used when connecting over TLS (use 'rediss://' as scheme in address).
--storage.redis.username string Redis database username. If set, it overrides the username in the connection URL.
--strictmode When set, insecure settings are forbidden.
--tls.certheader string Name of the HTTP header that will contain the client certificate when TLS is offloaded.
--tls.offload tls.certheader Whether to enable TLS offloading for incoming connections. If enabled tls.certheader must be configured as well.
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts status
Shows the status of the Nuts Node.
nuts status [flags]
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
-h, --help help for status
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vcr list-trusted
List trusted issuers for given credential type
nuts vcr list-trusted [type] [flags]
-h, --help help for list-trusted
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vcr list-untrusted
List untrusted issuers for given credential type
nuts vcr list-untrusted [type] [flags]
-h, --help help for list-untrusted
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vcr trust
Trust VCs of a certain credential type when published by the given issuer.
nuts vcr trust [type] [issuer DID] [flags]
-h, --help help for trust
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vcr untrust
Untrust VCs of a certain credential type when published by the given issuer.
nuts vcr untrust [type] [issuer DID] [flags]
-h, --help help for untrust
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vdr add-keyagreement
Add a key agreement key to the DID document. It must be a reference to an existing key in the same DID document, for instance created using the addvm command. When successful, it outputs the updated DID document.
nuts vdr add-keyagreement [KID] [flags]
-h, --help help for add-keyagreement
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vdr addvm
Add a verification method key to the DID document.
nuts vdr addvm [DID] [flags]
-h, --help help for addvm
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vdr conflicted
Print conflicted documents and their metadata
nuts vdr conflicted [flags]
--document Pass 'true' to only print the document (unless other flags are provided as well).
-h, --help help for conflicted
--metadata Pass 'true' to only print the metadata (unless other flags are provided as well).
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vdr create-did
Registers a new DID
nuts vdr create-did [flags]
--assertionMethod Pass 'false' to disable assertionMethod capabilities. (default true)
--authentication Pass 'true' to enable authentication capabilities.
--capabilityDelegation Pass 'true' to enable capabilityDelegation capabilities.
--capabilityInvocation Pass 'false' to disable capabilityInvocation capabilities. (default true)
--controllers strings Comma-separated list of DIDs that can control the generated DID Document.
-h, --help help for create-did
--keyAgreement Pass 'true' to enable keyAgreement capabilities.
--selfControl Pass 'false' to disable DID Document control. (default true)
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vdr deactivate
Deactivate a DID document based on its DID
nuts vdr deactivate [DID] [flags]
-h, --help help for deactivate
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vdr delvm
Deletes a verification method from the DID document.
nuts vdr delvm [DID] [kid] [flags]
-h, --help help for delvm
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vdr resolve
Resolve a DID document based on its DID
nuts vdr resolve [DID] [flags]
--document Pass 'true' to only print the document (unless other flags are provided as well).
-h, --help help for resolve
--metadata Pass 'true' to only print the metadata (unless other flags are provided as well).
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
nuts vdr update
Update a DID with the given DID document, this replaces the DID document. If no file is given, a pipe is assumed. The hash is needed to prevent concurrent updates.
nuts vdr update [DID] [hash] [file] [flags]
-h, --help help for update
--address string Address of the remote node. Must contain at least host and port, URL scheme may be omitted. In that case it 'http://' is prepended. (default "localhost:1323")
--timeout duration Client time-out when performing remote operations, such as '500ms' or '10s'. Refer to Golang's 'time.Duration' syntax for a more elaborate description of the syntax. (default 10s)
--verbosity string Log level (trace, debug, info, warn, error) (default "info")
Database Configuration
The Nuts node supports different backends for storage. This page describes the particulars of each backend and how to configure it.
Note
The node does not automatically back up your data or keys. This is something you need to set up regardless the backend you use.
Note
Clustering is not supported. Even if you use a backend that supports concurrent access (e.g. Redis), you can’t have multiple Nuts nodes use the same data storage.
Data
Data is everything your node produces and stores, except private keys. It is also everything that is produced and published by other nodes in the network.
BBolt
By default, all data (aside from private keys) is stored on disk using BBolt. You don’t need to configure anything to get it working, but don’t forget the backup procedure. If an alternative data store is configured (e.g. Redis), volatile data (projections that are generally quick to rebuild) is still stored in BBolt. You can back up volatile data, but it is not required.
Redis
If the node is configured to use Redis it stores all non-volatile data in the configured Redis server.
To use Redis, configure storage.redis.address
.
You can configure username/password authentication using storage.redis.username
and storage.redis.password
.
If you need to prefix the keys (e.g. you have multiple Nuts nodes using the same Redis server) you can set storage.redis.database
with an alphanumeric string. All keys written to Redis will then have that prefix followed by a separator.
You can connect to your Redis server over TLS by specifying a Redis connection URL in storage.redis.address
,
e.g.: rediss://database.mycluster.com:1234567
.
The server’s certificate will be verified against the OS’ CA bundle.
Make sure to configure persistence for your Redis server.
Private Keys
Your node generates and stores private keys when you create DID documents or add new keys to it. Private keys are very sensitive, if you leak them others could alter your presence on the Nuts network and possibly worse. If you lose them you need to re-register your presence on the Nuts network, which could be very cumbersome. Thus, it’s very important the private key storage is both secure and reliable.
Filesystem
This is the default backend but not recommended for production. It stores keys unencrypted on disk. Make sure to include the directory in your backups and keep these in a safe place. If you want to use filesystem in strict mode, you have to set it explicitly, otherwise the node fails during startup.
Vault
This storage backend is the recommended way of storing secrets. It uses the Vault KV version 1 store.
The prefix defaults to kv
and can be configured using the crypto.vault.pathprefix
option.
There needs to be a KV Secrets Engine (v1) enabled under this prefix path.
All private keys are stored under the path <prefix>/nuts-private-keys/*
.
Each key is stored under the kid, resulting in a full key path like kv/nuts-private-keys/did:nuts:123#abc
.
A Vault token must be provided by either configuring it using the config crypto.vault.token
or setting the VAULT_TOKEN environment variable.
The token must have a vault policy which has READ and WRITES rights on the path. In addition it needs to READ the token information “auth/token/lookup-self” which should be part of the default policy.
Migrating to Vault
Migrating your private keys from the filesystem to Vault is relatively easy: just upload the keys to Vault under kv/nuts-private-keys
.
Alternatively you can use the fs2vault
crypto command, which takes the directory containing the private keys as argument (the example assumes the container is called nuts-node):
docker exec nuts-node nuts crypto fs2vault /opt/nuts/data/crypto
In any case, make sure the key-value secret engine exists before trying to migrate (default engine name is kv
).
TLS Configuration
Connections between Nuts nodes are secured using mutual TLS (both client and server present a X.509 certificate). This applies to both gRPC and HTTP connections. Your TLS configuration depends mostly on where you terminate the TLS connection. This page describes the different layouts for TLS and how to configure them for gRPC.
Note
HTTP connections between nodes (all calls to /n2n
) must be secured using TLS which is not handled by the Nuts node.
You need to have a reverse proxy in front of the Nuts node for terminating the (node-to-node) HTTPS traffic and forwarding it to the Nuts node.
Refer to Interfaces/Endpoints for the requirements on this HTTP endpoint (and others).
In all layouts your node’s certificate must issued by a Certificate Authority, trusted by the other nodes in the network.
Each layout requires network.certfile
, network.certkeyfile
and network.truststorefile
to be configured.
You can also find working setups in the end-2-end test suite.
No TLS Offloading
By default, the TLS connection is terminated on the Nuts node. This means there is no system between the remote and local Nuts nodes that accepts TLS connections and forwards them as plain HTTP.
No additional configuration is required.
TLS Pass-through
When using a (level 4) load balancer that does not inspect or alter requests, TLS is still terminated on the Nuts node.
This set up does not need additional configuration.
Configuration for HAProxy could look like this:
listen grpc
bind *:5555
mode tcp
use_backend nuts_node_grpc
backend nuts_node_grpc
mode tcp
server node1 nodeA-backend:5555 check
Refer to the HAProxy documentation for more information.
TLS Offloading
In many setups TLS is terminated on a reverse proxy in front of the backend services over plain HTTP (HTTP/2 in our case).
To configure this setup your proxy needs to support HTTP/2 or gRPC traffic. Your proxy must add the TLS client certificate as request header. The certificate must be in PEM format and URL encoded.
In addition to the general TLS configuration, you need to configure the following options:
tls.offload
needs to be set toincoming
tls.certheader
needs to be set to the name of the header in which your proxy sets the certificate (e.g.X-SSl-CERT
). The certificate must in be PEM or base64 encoded DER format.
The certificate and truststore will still need to be available to the Nuts node for making outbound connections.
For NGINX the proxy configuration could look as follows:
upstream nuts-node {
server nuts-node:5555;
}
server {
server_name nuts;
listen 5555 ssl http2;
ssl_certificate /etc/nginx/ssl/server.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_client_certificate /etc/nginx/ssl/truststore.pem;
ssl_verify_client on;
ssl_verify_depth 1;
location / {
grpc_pass grpc://nuts-node;
grpc_set_header X-SSL-CERT $ssl_client_escaped_cert;
}
}
For HAProxy the proxy configuration could look as follows:
frontend grpc_service
mode http
bind :5555 proto h2 ssl crt /certificate.pem ca-file /truststore.pem verify required
default_backend grpc_servers
backend grpc_servers
mode http
http-request set-header X-SSL-CERT %{+Q}[ssl_c_der,base64]
server node1 nuts_node:5555 check proto h2
No TLS
You can disable TLS by setting network.enabletls
to false
, but this feature is only meant for development/demo purposes.
It should never be used in a production environment. If you disable TLS, you can only connect to nodes that have disabled TLS as well.
Configuring for Production
Running a Nuts node in a production environment has additional requirements regarding security and data integrity compared to development or test environments. This page instructs how to configure your node for running in a production environment and what to consider.
Persistence
Make sure your database configuration is set up to backup your node’s data and private keys.
Strict mode
By default the node runs in a mode which allows the operator run configure the node in such a way that it is less secure. For production it is recommended to enable strictmode which blocks some of the unsafe configuration options (e.g. using the IRMA demo scheme).
HTTP Interface Binding
By default all HTTP endpoints get bound on :1323 which generally isn’t usable for production, since some endpoints are required to be accessible by the public and others only meant for administrator or your own XIS. You can determine the intended public by looking at the first part of the URL.
Endpoints that start with /public should be accessible by the general public,
/internal is meant for XIS application integration and administrators.
It’s advisable to make sure internal endpoints aren’t reachable from public networks. The HTTP configuration facilitates this by allowing you to bind sets of endpoints to a different HTTP port. This is done through the http configuration:
http:
# The following is the default binding which endpoints are bound to,
# which don't have an alternative bind specified under `alt`. Since it's a default it can be left out or
# be used to override the default bind address.
default:
address: :1323
alt:
# The following binds all endpoints starting with `/internal` to `internal.lan:1111`
internal:
address: internal.lan:1111
# The following binds all endpoints starting with `/public` to `nuts.vendor.nl:443`
public:
address: nuts.vendor.nl:443
# The following enables cross-domain requests (CORS) from irma.vendor.nl
cors:
origin:
- irma.vendor.nl
# The following binds all endpoints starting with `/status` to all interfaces on `:80`
status:
address: :80
Cross Origin Resource Sharing (CORS)
In some deployments CORS can be required for the public IRMA authentication endpoints when the user-facing authentication page is hosted on a (sub)domain that differs from Nuts Node’s IRMA backend. CORS can be enabled on a specific HTTP interface by specifying the domains allowed to make CORS requests as cors.origin (see the example above). Although you can enable CORS on the default endpoint it’s not advised to do so in a production environment, because CORS itself opens up new attack vectors on node administrators.
Diagnostics
To aid problem diagnosis every node in a network should share some information about itself; the type and version of software it’s running, which peers it is connected to and how long it’s been up. This helps others diagnosing issues when others experience communication problems with your, and other nodes. Although discouraged, this can be disabled by specifying 0 for network.advertdiagnosticsinterval.
Decentralized identifiers
Nuts uses W3C Decentralized Identifiers as a base for tracking identities. From the W3C website:
Decentralized identifiers (DIDs) are a new type of identifier that enables verifiable, decentralized digital identity. A DID identifies any subject (e.g., a person, organization, thing, data model, abstract entity, etc.) that the controller of the DID decides that it identifies. In contrast to typical, federated identifiers, DIDs have been designed so that they may be decoupled from centralized registries, identity providers, and certificate authorities. Specifically, while other parties might be used to help enable the discovery of information related to a DID, the design enables the controller of a DID to prove control over it without requiring permission from any other party. DIDs are URIs that associate a DID subject with a DID document allowing trustable interactions associated with that subject.
Within Nuts, DIDs identify both care organizations and software vendors. All DID methods require their own specification. The Nuts specification for the nuts DID method can be found on https://nuts-specification.readthedocs.io. Nuts DIDs are designed so they represent public/private key pairs. Any party can generate and claim a key pair. Only when information is added to the key pair, the key pair becomes important.
DIDs can gather claims through Verifiable Credentials. This allows a DID to actually represent something known in real life. For example: adding an organization name credential connects the key pair to the name of the organization. It connects the digital world to the real world.
DID Documents
DIDs are backed by a DID Document. It defines the public keys, who can alter the document and any services related to the DID. DID documents are automatically propagated through the network when they are created. When DID documents are created, the DID always represents the public key fingerprint of the associated key. A DID document is always created with a new key, the holder of the key can delegate the control to another DID.
Controller
The controller of the DID document is the only one that can change the contents. It can assign other controllers, change keys, change services and revoke the DID. When created, the DID document only has a single controller: the DID itself and the key related to it. It can choose to change add new controllers and remove existing ones. Changes to DID documents are only accepted when the network transaction is signed with a controller’s authentication key.
Verification Method
All public keys within a Nuts DID Document are listed under verificationMethod.
Assertion Method
Keys referenced from the assertionMethod section are used to sign JWTs in the OAuth flow and for issuing Verifiable Credentials.
Authentication Method
Keys referenced from the authentication section are used to change the DID document and sign network transactions.
Services
The services section is used to list service endpoints. There are some endpoints that are shared amongst all services, like the oauth service. But most service endpoints will be coming from specific Bolts.
Nuts node development
Requirements
Go >= 1.18 is required.
Building
Just use go build
.
Building for exotic environments
You can build and run the Nuts node on more exotic environments, e.g. Raspberry Pis:
32-bit ARMv6 (Raspberry Pi Zero):
env GOOS=linux GOARCH=arm GOARM=6 go build
Running tests
Tests can be run by executing
go test ./...
Code Generation
Code generation is used for generating mocks, OpenAPI client- and servers, and gRPC services.
Make sure that GOPATH/bin
is available on PATH
and that the dependencies are installed
Install protoc
:
MacOS:brew install protobuf
Linux:apt install -y protobuf-compiler
Install Go tools:
make install-tools
Generating code:
To regenerate all code run the run-generators
target from the makefile or use one of the following for a specific group
Group |
Command |
---|---|
Mocks |
|
OpenApi |
|
Protobuf + gRCP |
|
All |
|
Docs Generation
To generate the documentation, you’ll need to build a docker image and run it from the docs directory:
cd docs
docker build -t nutsfoundation/nuts-node-docs .
docker run --rm -v $PWD:/docs nutsfoundation/nuts-node-docs make html
README
The readme is auto-generated from a template and uses the documentation to fill in the blanks.
make gen-readme
Documentation
The documentation can be build by running the following command from the /docs
directory:
make html
Releasing Nuts Node
Nuts Node and auxiliary tools/applications follow a semantic versioning scheme (<major>.<minor>.<patch>
):
Given a version number MAJOR.MINOR.PATCH, increment the: 1. MAJOR version when you make incompatible API changes, 2. MINOR version when you add functionality in a backwards compatible manner, and 3. PATCH version when you make backwards compatible bug fixes.
(Taken from semver.org)
Note: “API” is a broad term, it covers every interface interacted with by applications or other nodes (including Nuts network protocols).
Aside from the Nuts Node itself, the projects below need to be released. They follow the major version from the Nuts Node, but minor and patch versions may differ.
Major release
A major release starts with version number <major>.0.0
. Every Nuts Node release has a name (e.g. “Brazil”) and a version number.
A release consists of a Git tag and a release in Github with release notes. Releases are created according to the following format:
Git tag:
v<major>.<minor>.<patch>
, e.g.v2.0.0
Release name:
<name> release (<version>)
, e.g.:Brazil release (v2.0.0)
(every release has a designated name)Release notes: auto-generated by Github.
Bugfix release/patches
When an issue is fixed in a released version a bugfix/patch version must be released.
The bug must be fixed on a branch named after the major version, e.g. v1
or v2
.
The release name follows the release name, but is named “bugfix” instead of “release”. E.g.: Brazil bugfix (v2.0.1)
.
Backports
Bugfixes often need to be backported, e.g. it’s fixed on the master
branch but also need to be fixed in the last version,
and maybe even in the before last version. Bugfix releases stemming from backports follow the same versioning and naming scheme as regular bugfix releases.
API development
When developing APIs, please follow these guidelines.
Contract first
The Nuts node APIs are specified in Open API Specification (OAS).
The files are located under /docs/_static/<engine>/<version>.yaml
.
Where <engine>
is a specific module like crypto
or auth
and <version>
defines the version of the API.
We use version 3.0.y
of the OAS.
Versioning
We use versioning of the APIs.
This is reflected in both the OAS files and the HTTP paths.
Versions must follow the pattern v
and start at v1
.
These are major versions, any breaking change results in a new major version of the API.
New additions, bug fixes and changes that are backwards compatible may be done in the current version.
Code generation
The OAS files are used for code generation. The makefile contains the gen-api
target which will generate the code.
The build target only needs to be extended when a new version or new engine is added.
Generated code is always placed in /<engine>/api/<version>/generated.go
.
Return codes
The error return values are generalized for all API calls.
The return values follow RFC7807.
The definition is available under /docs/_static/common/error_response.yaml
.
The error definition can be used in a OAS file:
paths:
/some/path:
get:
responses:
default:
$ref: '../common/error_response.yaml'
The error responses will not be listed as responses in the online generated documentation. To describe error responses, the specific responses need to be added to the API description:
paths:
/some/path:
post:
description: |
Some description on the API
error returns:
* 400 - incorrect input
Paths
The API paths are designed so different security schemes can be setup easily.
API paths follow the following pattern:
/<context>/<engine>/<version>/<action>
All paths start with a security <context>
:
/internal/**
These APIs are meant to be behind a firewall and should only be available to the internal infrastructure. All DID Document manipulation APIs fall under this category./n2n/**
These APIs must be available to other nodes from the network. This means they must be protected with the required client certificate as specified by RFC011. The creation of an access token is one example of such an API./public/**
These APIs must be publicly available on a valid domain. No security must be set. These APIs are used by mobile devices.
After the context, the <engine>
is expected. An engine defines a logical unit of functionality.
Each engine has its own OAS file. Then as discussed earlier, the <version>
is expected.
The last part is the <action>
, this part can be freely chosen in a RESTful manor.
Events
Each event consists of a single JSON encoded message that is categorized by its subject which in term are grouped into streams. Each stream defines how to handle limits, storage, data retention, deliverability etc.
Streams
Name |
Summary |
Policy |
Durable |
Message limit |
Storage |
---|---|---|---|---|---|
nuts-disposable |
Main event-stream |
When the stream is full old messages will be discarded |
No |
100 |
Memory |
Development with Vault
You can start a development Vault server as follows:
docker run --cap-add=IPC_LOCK -d -p 8200:8200 \
-e 'VAULT_DEV_ROOT_TOKEN_ID=unsafe' -e 'VAULT_ADDRESS=http://localhost:8200' \
--name=dev-vault \
vault
The server will start unsealed, with root token unsafe.
Now log in and enable a key-value secret engine names kv:
docker exec -e 'VAULT_ADDR=http://0.0.0.0:8200' dev-vault vault login
Enter the root token unsafe, then enable the kv engine:
docker exec -e 'VAULT_ADDR=http://0.0.0.0:8200' dev-vault vault secrets enable -path=kv kv
Then configure the Nuts node to use the Vault server:
crypto:
storage: vaultkv
vault:
address: http://localhost:8200
token: unsafe
Contribute
If you want to contribute to any of the nuts foundation projects or to this documentation, please fork the correct project from Github and create a pull-request.
Documentation contributions
Documentation is written in Restructured Text. A CheatSheet can be found here.
You can test your documentation by installing the required components.
Documentation initialisation
When starting a new project, the documentation can be initialised using:
sphinx-quickstart docs
This will start the interactive setup of sphinx with a document root at docs. For Nuts projects we use that specific directory for documentation in a code project. You might have noticed that the nuts-documentation repo uses the root directory as documentation root.
Most defaults will do, although we use intersphinx to go back-and-forth between the different sub-projects.