where the tangle info will be stored content.tangles[tangle]
defaults to type.split('/')[0]
spec.typePatternString
a regex pattern string which can be used to over-write the default. Useful if you've used regex characters that need escaping in you spec.type (e.g. *)
defaults to : "^" + spec.type + "
quot;
spec.staticPropsObject
a list of props which are not mutable, and will be baked into root message
a function run before writing and during reading to confirm the validity of message extending the tangle
signature: fn(context, msg) => Boolean where
context = { accT, graph } where accT is the accumulated transform for the position immediately before this message in the tangle, and graph is a @tangle/graph instance for the tangle so far.
msg the message being assessed
used by:
create to check the message about to be published is valid. In this case accT = I, the (empty) identity transform.
update to check the message about to be published is valid given the existing tangle state it would extend
read to determine which messages are valid to include in reducing
NOTE - in create you don't have access to context.graph, as there's no graph yet
spec.hooksObject with properties:
isRootArray a collection of validator functions which each root message must pass to proceed
isUpdateArray a collection of validator functions which each update message must pass to proceed
NOTE these validators are expected to have signature isValid (content) => true|Error
spec.getTransformationFunction
a function which is used to map a full message ({ key, value }) to a transformation
default: m => m.value.content
this is particularly useful for coercing legacy messages into a format your spec can process
WARNING: create a new object if you plan to mutate the shape of a message, otherwise ssb-crut will break
spec.arbitraryRootBoolean
default: false
if true, then the tangle root of the record can be any messageId
this allows you to attach (for example) a settings record to a group root message
disables crut.create (as you already have your root created), so you start with crut.update
during tangle reducing, an empty root node with the right shape is fabricated
crut.create(allProps, cb)
Makes a new record, and calls back with the id of that record.
allPropsObject
none/ some/ all of the properties declared in spec.props
none/ some/ all of the properties declared in spec.staticProps
recpsArray (optional) a list of recipients who this record will be encrypted to. Once this is set on create, it cannot be updated
allowPublicBoolean (optional) for if you have ssb-guard-recps installed and want to explicitly allow a public message through
Notes:
if cb is not passed, a Promise is returned instead.
crut.read(id, cb)
Takes a record id and calls back with a Record.
A tangle here is a collection of messages linked in a directed acyclic graph.
Each of thee messages contains an "operational transform" which is an
instuction about how to update the record state.
Transformations are concatenated (added up) while traversing the graph.
For a tangle made up of messages linked like this:
A << root
/ \
B C << concurrent updates
|
D << an update which is also a tip
Then the reduced Record would look like:
{
key: A, // the key of the tangle root message
type,
...staticProps, // any staticProp values
states: [
{
key: D, // key of tangle tip message
...props // reified state of props for this tangle tip
},
{
key: B, // key of tangle tip message
...props // reified state of props for this tangle tip
}
}
}
There will be 1 or more "states" depending on whether the tangle is a in a
branched / forked state at the moment.
The state of the props returned are "riefied" (meaning has been made real),
because often the transformation format is optimised for mathematical properties,
but not very human friendly.
Notes:
states is sorted "most recent" to "least recent" by the tip messages's declared timestamp.
if cb is not passed, a Promise is returned instead.
crut.update(id, props, cb)
Updates record id.
propsObject can have properties
all/ some/ none of the props declared in spec.props
allowPublicBoolean (optional) for if you have ssb-guard-recps installed and want to explicitly allow a public message through
The props provided are used to generate a transformation which is then checked with isValidUpdate (if provided), they are:
Message contents are also checked against isUpdate before publishing.
Calls back with the key of the update message published.
Notes:
if cb is not passed, a Promise is returned instead.
by default, updates are accepted from everyone. To change this, specifiy behaviour in isValidUpdate e.g.
spec.isValidUpdate = (context, msg) => {
const { accT, graph } = context
if (!graph) return true
// crut.read has graph, but crut.update doesn't yet
// this means updates from others can be published but will be ignored
return graph.rootNodes.some(root => {
return root.value.author === msg.value.author
})
}
crut.tombstone(id, opts, cb)
A convenience helper mainly here to put the T in CRUT.
optsObject with properties:
reasonString (optional) give a reason for why you're tombstoning the record
undoBoolean (optional) set to true to remove the tombstone
allowPublicBoolean (optional) for if you have ssb-guard-recps installed and want to explicitly allow a public message through
Calls back with the key of the update message which tombstoned.
Notes:
if cb is not passed, a Promise is returned instead.
Using spec.arbitraryRoot with Private Groups
If you set spec.arbitraryRoot to true, then you can use a groupId that you're a part of and ssb-crut
will automatically root your record at the group init message.
To use this feature you must be using ssb-tribes >= 2.7.0
The methods you can do this with are:
crut.updateGroup(groupId, props, cb)
crut.readGroup(groupId, cb)
crut.tombstoneGroup(groupId, opts, cb)
Reminder there is no crut.create when you have an arbitraryRoot
TODO
crut.update does not currently publish merges
currently extends the tip with most recent activity
want to change this in the future but @tangle/reduce will needs more work