@keeex/stories-sdk
SDK to provide access to the KeeeX Stories customer API "Chains-X".
This SDK provides some level of abstraction over the base API; when applicable these difference are noted below. The actual "Chains-X" API is documented here: Chains-X.
Short description of Stories
KeeeX Stories is a data registry that is used to record series of chronological events collected in "stories". A story is identified by three things:
- a community: communities are large repositories of data attached to a customer or shared for s specific purpose. A single API key allow access to one community.
- a scope: scopes are categories stored under a community. While there is no restrictions on scopes, they are usually limited to identifying different kinds of stories.
- identifiers: each story have at least one identifier, which is a string unique to this community and scope pairing. It is possible to append multiple identifiers to the same story, keeping in mind the uniqueness restriction.
Each chronological event in a story can be attached to a keeexed file (stored externally) and have it's own set of metadata, publicly visible to everyone with a valid access to the KeeeX Stories service.
Some example of community/scope/identifiers:
- community would be a customer name for a private usage, or something like "ship_logistics_project" for a project involving multiple actors creating a shared logistic service.
- scopes could be things like "product", "box", "factory" to track physical things, or "shipment", "report", "pictures" to track a sequence of event or items relative to other stories.
- identifier needs to be unique per scope and community. This usually lead to using unique, long-lived identifiers when interfacing with existing systems, serial numbers, etc.
Each link in a story can have metadata attached to it. These data can be retrieved by anyone with access to the service. They are usually used either for public data, or to indicate the nature of the content referenced by the link.
Using this SDK, the metadata have an additional layer of formatting; the raw metadata written on the
KeeeX Stories service is comprised of an object with two properties: public
containing the actual
public metadata provided, and links
as described in the paragraph below.
This extra layer is handled automatically through the SDK but is important to know if you perform
direct calls to the service's API.
Finally, a provision to create links between stories have been normalised to help take advantages of such references. This SDK accept both references and IDX as references, but ultimately the stored format only keeps references through scope names and link's IDX.
Data authenticity
The "Chains-X" API provides two ways to authenticate data. The first option is to leave the server to create authentication data. This mean that data will be signed by the server, and provides limited authenticity against third parties.
The second option is to provide user-side digital signatures, meaning that each link created in Stories will be authenticated by a digital identity under the sole control of the caller and not the KeeeX Stories service's operator.
This SDK abstract the required calls to provide digitally signed entries in single-call methods that takes as their parameters either nothing (to perform server-side signature), a key in the format of KeeeX's key library, or a basic callback to perform digital signature in a compatible way through other means.
Data verification
Each link in KeeeX Stories lead to the creation of metadata files that are keeexed, timestamped and
digitally signed (see above).
These files includes cross-references between them making it very difficult to tamper without traces
once created.
There is a fetch feature that allows retrieving these files to keep as verifiable proof.
You can pass the fullData
parameter to fetch()
and latest()
.
SDK features
The SDK is fully documented using TypeScript and JSDoc-compatible comments. Any recent IDE should pick details for each methods; the documentation here simply underlines the name of the methods and their expected flow.
Initialize the SDK
To use the SDK, it must be first initialized with an API key provided by KeeeX. This API key provides full access to a single community.
import Sdk from "@keeex/stories-sdk/lib/index.js";
const sdk = new Sdk({apiKey: "api key"});
Create a story
To create a new story the create()
method is used.
The first parameter are the details of the new story.
An optional second parameter can be used for client-side authentication by providing a key.
const res = await sdk.create({scope: "ship", identifier: "ref1"}, someKey);
Adding a node to a story
To append a new entry to a story you should use the insert()
method.
Similar to create()
it takes two parameters, the second being an optional key.
const res = await sdk.insert({
story: {scope: "ship", reference: "ref1"},
idx: "xuseg-nerim-vizit-tesas-gifih-ciruc-figig-bacan-lulol-bizyd-zagif-macem-kumuc-navyt-mosev-polof-zyxix",
publicMetadata: {
filetype: "picture",
content: "container damages",
geolocation: "-21.120807,79.974954",
url: "https://some.service.invalid/getFileByIDX",
}
});
The above exemple uses a mock-up of something that could be used to provide some details about a file while keeping the actual data behind a private portal.
To add cross-references between stories, it is possible to use the links
metadata, although usage
and meaning is application dependant:
const res = await sdk.insert({
story: {scope: "pictures", reference: "someRef"},
idx: "xedir-tisih-subul-ficas-nypel-gisif-picol-sotaf-lygyb-gukad-mekuc-henuc-sevom-sizeb-sovut-mygac-soxex",
publicMetadata: {
filetype: "picture",
content: "production site",
geolocation: "-80.912656,111.632091",
url: "https://some.service.invalid/getFileByIDX",
},
references: {
"ship": "ref1",
},
});
A link created/inserted with the references
property set will indicate upon reading a reference to
the stories with the given scope (key) and identifier (string or array of strings).
The identifiers can be link's IDX, data IDX or existing text identifiers.
Retrieving data from a story
Using the fetch()
and list()
methods it is possible to retrieve data from a story.
Both methods takes the same input parameters but the list()
method only enumerate each link's IDX
while the fetch()
method returns the metadata and other details.
These methods have pagination, and the return value includes a next
property that can be used to
fetch the remaining data.
// Fetch from the start
const res1 = await sdk.fetch({type: "story", scope: "ship", reference: "ref1", limit: 10});
// Fetch backward from the end
const res2 = await sdk.fetch({type: "storyEnd", scope: "ship", reference: "ref1", limit: 10});
// Fetch forward from a specific link
const res3 = await sdk.fetch({type: "idx", scope: "ship", idx: "xevol-gilic-vybyd-resez-lopyb-fyzyk-domod-gylur-cymyf-tuvek-zehut-nucog-nimug-nigon-pidig-paruv-zoxux", limit: 10, reverse: false});
Marking a link as deleted
Links can be marked as deleted. They are not actually deleted from the service, but the date, reason, and author of the deletion (if applicable) are stored and cross-referenced.
const res = await sdk.delete({idxOrReference: {scope: "ship1", reference: "ref1"}}, someKey);
Sequential access
The KeeeX Stories service provides some basic enumeration capabilities, allowing listing of known stories for a scope and accessing a link through its index in its story. These capabilities are limited and relatively expensive as they require reading a whole story up to the requested point. The SDK provides some level of caching over these features, but initial access still incurs a lot of read operations.
Here's an example of using these features through the SDK:
const storyCount = await sdk.getScopeStoryCount("scope2");
// Return the identifier of the fourth created story in the scope "scope2"
const someStoryIdentifier = await sdk.getScopeStory("scope2", 3);
const storyLength = await sdk.getStoryLength({scope: "scope2", reference: "test1"})
// Return the content of the third link (starts at 0)
const elem = await sdk.getStoryEntry({scope: "scope2", reference: "test1"}, 2);