pub-keystore
A simple store for public keys in Node.js.
- Index keys by URI and issuer IDs.
- Listen to key updates.
- Backed by PouchDB, CouchDB 2, SQLite, PostgreSQL or memory.
- Keys can be in any format (or even not keys!).
- Supports access from multiple processes.
- Full set of unit tests.
Example:
var pub_keystore = require('pub-keystore');
var assert = require('assert');
var uri = 'mailto:dave@davedoesdev.com';
var pub_key = 'some key data';
pub_keystore({ db_type: 'pouchdb', db_for_update: true, no_changes: true }, function (err, ks1)
{
pub_keystore({ db_type: 'pouchdb', keep_master_open: true }, function (err, ks2)
{
var the_issuer_id, the_rev;
ks2.on('change', function (id, rev)
{
assert.equal(id, uri);
assert.equal(rev, the_rev);
ks2.get_pub_key_by_issuer_id(the_issuer_id, function (err, pub_key2, uri2, rev2)
{
assert.equal(pub_key2, pub_key);
assert.equal(uri2, uri);
assert.equal(rev2, the_rev);
console.log("done");
});
});
ks1.add_pub_key(uri, pub_key, function (err, issuer_id, rev)
{
the_issuer_id = issuer_id;
the_rev = rev;
ks1.deploy();
});
});
});
The API is described here.
Installation
npm install pub-keystore
Licence
Test
grunt test
Code Coverage
grunt coverage
c8 results are available here.
Coveralls page is here.
Lint
grunt lint
CLI
In the cli
directory are some command line utilities which call into the API. They do simple things like creating stores and adding and removing public keys. I hope you find them useful but at the very least they should prove good examples of how to call the API.
API
Source: docs.js
Opening a key store
PubKeyStore
Adding and removing keys
Retrieving keys
- PubKeyStore.prototype.get_pub_key_by_uri
- PubKeyStore.prototype.get_pub_key_by_issuer_id
- PubKeyStore.prototype.get_issuer_id
- PubKeyStore.prototype.get_uris
Lifecycle
Replication (PouchDB only)
Events
- PubKeyStore.events.change
- PubKeyStore.events.error
- PubKeyStore.events.replicated
- PubKeyStore.events.replicate_error
module.exports(config, cb)
Opens a public keystore.
Parameters:
-
{Object} config
Configures the keystore. Valid properties:-
{String} db_type
The type of database to use for backing the store. You must supplypouchdb
,couchdb
,sqlite
,pg
orin-mem
. -
{String} [db_name]
(db_type='pouchdb'
ordb_type='couchdb'
) Name of database to use for storing keys. Defaults topub-keys
. -
{Boolean} [db_already_created]
(db_type='pouchdb'
ordb_type='couchdb'
) If falsey then the database will be created. This is an idempotent operation so it doesn't matter if the database has already been created. However, if you know the database already exists then you can passtrue
. Defaults tofalse
. If the database doesn't exist and you don't have permission to create it thencb
will receive an error. You must create SQLite and PostgreSQL databases beforehand. For SQLite, use a copy ofsql/pub-keystore.empty.sqlite3
. -
{Boolean} [no_changes]
Don't emitchange
events when a key is changed. Defaults tofalse
(i.e. do emitchange
events). -
{Boolean} [verbose]
Write key changes, warnings and errors toconsole
. Defaults tofalse
. -
{Boolean} [no_updates]
Don't allowadd_pub_key
to add a key if one already exists for a URI. That is, each URI can only be associated with a public key once and the association cannot be updated. Defaults tofalse
. -
{Boolean} [db_for_update]
(db_type='pouchdb'
) PouchDB can only write to a database from one process at a time. If you want to run multiple processes against the same keystore,pub-keystore
can work around this by writing to a master database and then replicating it to multiple reader databases (one for each process). When you're updating keys, passdb_for_update=true
to write to the master database. Make sure youdeploy
and close the master database after updating it so that your reader processes can open it for replication. Defaults tofalse
. -
{String} [deploy_name]
(db_type='pouchdb'
) Name of the replica database to use for the current process (whendb_for_update=false
). Make sure you specify a differentdeploy_name
for each process running against the same keystore. Defaults todefault
. -
{String} [db_dir]
(db_type='pouchdb'
) Where to write the PouchDB database files. Defaults to a directory namedpouchdb/store/<db_name>
in thepub_keystore
module directory. -
{Boolean} [no_initial_replicate]
(db_type='pouchdb'
) Whether to skip initial replication from the master database. Defaults tofalse
. Note that replication will still occur whenever the master database is updated anddeploy
ed. -
{Boolean} [keep_master_open]
(db_type='pouchdb'
) Normally the master database is closed after replicating from it so that it can be updated or replicated from other processes. However, if you want to use master and replica databases from a single process then you'll need to specifykeep_master_open=true
to stop PouchDB getting confused. Defaults tofalse
. Normally if only a single process is accessing the keystore then you can just open one instance withdb_for_update=true
. -
{Boolean} [persistent_watch]
(db_type='pouchdb'
) Reader processes monitor a shared file to know whendeploy
has been called on the master database, usingfs.watch
. By default, the watch isn't persistent so it won't keep your process open if nothing else is going on. Passpersistent_watch=true
to make it persistent. -
{String} [replicate_signal]
(db_type='pouchdb'
) Name of a Unix signal (e.g.SIGUSR2
) which can be sent to a reader process to force a replication from the master database. Defaults toundefined
(no signal will be listened to). Replication normally happens whendeploy
is called from the writing process orreplicate
is called from the reading process. -
{String} [db_host]
(db_type='couchdb'
) URL of the CouchDB server. Defaults tohttp://127.0.0.1
. -
{Integer} [db_port]
(db_type='couchdb'
) Port number of the CouchDB server. Defaults to5984
. -
{String} [ca]
(db_type='couchdb'
) When connecting using HTTPS, an authority certificate or array of authority certificates to check the remote host against. Defaults toundefined
(no checking will be performed). -
{String} [username]
(db_type='couchdb'
) If you need to authenticate to your CouchDB server (e.g. to gain database update rights) then specify the name of the user here. Defaults toundefined
(anonymous access). Note that users updating the CouchDB database must have thedb_name-updater
role, wheredb_name
is the name of the database (see above, the default role required ispub-keys-updater
). -
{String} [password]
(db_type='couchdb'
) If you need to authenticate to your CouchDB server (e.g. to gain database update rights) then specify the user's password here. Defaults toundefined
(anonymous access). -
{Integer} [maxSockets]
(db_type='couchdb'
) Maximum number of concurrent sockets that can be opened to the CouchDB server. Defaults toInfinity
. -
{Integer} [busy_wait]
(db_type='sqlite'
ordb_type='pg'
) Number of milliseconds to wait for retrying if another keystore has the database file locked or is performing a transaction. Defaults to 1000. -
{Integer} [check_interval]
(db_type='sqlite'
ordb_type='pg'
) Number of milliseconds between checking the database for changes. Defaults to 1000. -
{String} db_filename
(db_type='sqlite'
) Filename in which to store public keys. You should use a copy ofsqlite/pub-keystore.empty.sqlite3
. -
{Integer} [db_mode]
(db_type='sqlite'
) Mode to open the file in. See the sqlite3 documentation. -
{Object} db
(db_type='pg'
)node-postgres
configuration.
-
-
{Function} cb
Function called with the result of opening the keystore. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
. Note that for PouchDB-backed stores, if the database is already open by another process for update or replication, you will receive an error. It's up to you to retry as appropriate for your application. -
{PubKeyStore} ks
ThePubKeyStore
object. Note that in the case of an error occuring after the store has been open but before a successful changes feed has been established, you may receiveerr
andks
.
-
PubKeyStore.prototype.add_pub_key(uri, pub_key, [options], [cb])
Add a public key to the keystore.
Parameters:
-
{String} uri
A known, permanent identifier for the public key's owner. You can use anything but a URI seems ideal. For example if you know the owner's email address then you could you amailto
URI (e.g.mailto:dave@davedoesdev.com
). -
{String | Object} pub_key
The public key itself. This can be in any format (e.g. PEM). -
{Object} [options]
Additional options. Valid properties:-
{Boolean} [allow_update]
If you passedno_updates=true
when opening the keystore, you can override it here by passingtrue
, which allows this call to update an existing public key.
-
-
{Function} [cb]
Function to call once the key has been added. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
. -
{String} issuer_id
A unique, hex-encoded, random string which you can use as an alternative when retrieving the public key. This is useful if you want to give out an identifier for the key without revealing its owner. Note that every time you add a key, a newissuer_id
will be generated. If a key already exists for theuri
then it will be overwritten. -
{String} rev
A revision string for the key. Like theissuer_id
, this will change every time a key is added. Unlike theissuer_id
, it is sent withchange
events so you if you're caching keys then you can tell whether the cached version is up-to-date.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.remove_pub_key(uri, [cb])
Remove a public key from the keystore.
Parameters:
-
{String} uri
The permanent identifier you gave to the key when adding it usingadd_pub_key
. -
{Function} [cb]
Function to call once the key has been removed. It will receive the following argument:-
{Object} err
If an error occurred then details of the error, otherwisenull
. A non-existent key is not treated as an error.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.get_pub_key_by_uri(uri, cb)
Retrieve a public key using its permanent identifier (URI).
Parameters:
-
{String} uri
The permanent identifier you gave to the key when adding it usingadd_pub_key
. -
{Function} cb
Function to call with the result. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
. A non-existent key is not treated as an error. -
{String|Object} pub_key
The public key for theuri
, ornull
if it wasn't found. -
{String} issuer_id
The current unique, random string you can use to retrieve the key usingget_pub_key_by_issuer_id
. -
{String} rev
The current revision string for the public key.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.get_pub_key_by_issuer_id(issuer_id, cb)
Retrieve a public key using its unique, random identifier.
Parameters:
-
{String} issuer_id
The unique identifier for the key. -
{Function} cb
Function to call with the result. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
. A non-existent key is not treated as an error. -
{String|Object} pub_key
The public key for theissuer_id
, ornull
if it wasn't found. -
{String} uri
The permanent identifier you gave to the key when adding it usingadd_pub_key
. -
{String} rev
The current revision string for the public key.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.get_issuer_id(uri, cb)
Get a unique, random identifier for a public key.
Parameters:
-
{String} uri
The permanent identifier you gave to the key when adding it usingadd_pub_key
. -
{Function} cb
Function to call with the result. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
. A non-existent key is not treated as an error. -
{String} issuer_id
The current unique, random string you can use to retrieve the key usingget_pub_key_by_issuer_id
, ornull
if it wasn't found. -
{String} rev
The current revision string for the public key.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.get_uris(cb)
Get a list of all of the public key URIs in the store.
Parameters:
-
{Function} cb
Function to call with the result. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
. -
{Array} uris
URIs of all the public keys in the store.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.close([cb])
Close the store and its backing database.
Parameters:
-
{Function} [cb]
Function to call once the database has been closed. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.create([cb])
Create the store's backing database.
Unless you pass db_already_created=true
when opening the keystore, this method is automatically called for you when the store is opened. It is an idempotent operation so it doesn't matter if you call it twice.
For SQLite- and PostgreSQL-backed databases, this is a no-op: you must create the database beforehand. For in-memory databases, this is also a no-op.
Parameters:
-
{Function} [cb]
Function to call once the database has been created. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.destroy([cb])
Close the store and destroy its backing database. This will delete all public keys!
For SQLite- and PostgreSQL-backed databases, this deletes the keys but doesn't destroy the database.
Parameters:
-
{Function} [cb]
Function to call once the database has been destroyed. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.deploy([cb])
(PouchDB) Notify reader processes to replicate from the master database. You should call this when you've opened the keystore with
db_for_update=true
, performed some updates and want other processes reading from the store to receive the updates. Internally it usestouch
andfs.watch
on a shared file.
For CouchDB-, SQLite-, PostgreSQL- and memory-backed keystores, this is a no-op.
Parameters:
-
{Function} [cb]
Function to call once the shared file has beentouch
ed. Note this will be before reader processes finish replicating. It will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.prototype.replicate(opts, [cb])
(PouchDB) Force replication from the master database. Usually you shouldn't need to call this because reader processes (where the keystore is opened without
db_for_update=true
) will replicate when the keystore is opened and when they detect that a writer process has calleddeploy
.
For CouchDB-, SQLite-, PostgreSQL- and memory-backed keystores, this is a no-op.
Parameters:
-
{Object} opts
Replication options. Valid properties:-
{Boolean} no_retry
If replication fails (typically because the master database is open in another process also trying to replicate) then it is automatically retried after a random delay of between 1 and 2 seconds. Setno_retry
totrue
to disable this behaviour. Defaults tofalse
.
-
-
{Functon} [cb]
Function to call once replication has completed successfully (or failed if you setopts.no_retry=true
and an error occurred). Alternatively you can listen for thereplicated
event which is emitted on successful replication (for consistency, CouchDB-backed stores will raise this too, after the no-op).cb
will receive the following arguments:-
{Object} err
If an error occurred then details of the error, otherwisenull
.
-
Go: TOC | PubKeyStore.prototype
PubKeyStore.events.change(uri, rev, deleted)
change
event
Emitted when a public key is updated or removed from the keystore.
Parameters:
-
{String} uri
The permanent identifier for the key. -
{String} rev
The new revision string for the key. -
{Boolean} deleted
Whether the key has been removed from the store.
Go: TOC | PubKeyStore.events
PubKeyStore.events.error(err)
error
event
Emmited when an error occurs in the changes feed from the database. This may mean you receive no more change
events.
Parameters:
-
{Object} err
Details of the error.
Go: TOC | PubKeyStore.events
PubKeyStore.events.replicated(close_master)
replicated
event
Emitted when a successful replication from the master database completes (PouchDB-backed keystores). CouchDB-, SQLite-, PostgreSQL- and memory-backed stores emit this too for consistency, after replicate
is called.
Parameters:
-
{Function} close_master
Function you can call to close the master database if you setconfig.keep_master_open=true
when opening the keystore. This lets you control when to close the master database yourself. If you didn't setconfig.keep_master_open=true
thenclose_master
is a no-op.close_master
takes the following parameters:-
{Function} cb(err)
This will be called after the master database is closed (or after the no-op).
-
Go: TOC | PubKeyStore.events
PubKeyStore.events.replicate_error(err)
replicate_error
event
Emitted when replication from the master database fails (PouchDB-backed keystores only). This is emitted even when replication retry is enabled (i.e. if you didn't set no_retry=true
when opening the store).
Parameters:
-
{Object} err
Details of the error.
Go: TOC | PubKeyStore.events
—generated by apidox—