Installation

Plugins add behavior to the embedded library. Install the core library before selecting plugins.

Included plugins

PluginNamePurpose
Adaptive query planneradaptive_query_plannerLearn from query runs and compare alternative plans
ReactorsreactorsRun live mutations after commits
History retentionhistoryPurge history according to retention settings
Object storeobjectsStore binary payloads as named files and directories
BackupsbackupsCopy history and object payloads to local folders and S3-compatible storage, and recover from them
SDK generationsdkGenerate typed language APIs from stored definitions

The library's plugins operation lists the plugins it can use and their requirements. The coming soon list describes prototype features that are not available in this library.

FormPlugin setFilesChange the set
Plugin libraryOne shared library per plugin beside the core libraryThe plugin libraries in lib/<os>_<arch>/Use the plugin libraries from the same release as the core library
Compiled into the coreChosen when the core library is builtNoneRebuild the core library
Static archiveEvery plugin in the release, linked into your programNoneRelink with the static archive of another release

Release packages ship the adaptive planner, Reactors, SDK generation, the object store and backups as plugin libraries. History retention is compiled into the core library. The static archive in the same package contains all these plugins. In all three forms, a database enables plugins when it opens, as described in Enable plugins.

Plugin libraries

A release package keeps each platform's shared core library and plugin libraries together in that platform's directory, lib/<os>_<arch>/:

FileLinuxmacOSWindows
Core librarylibstardust.solibstardust.dylibstardust.dll
Plugin library, one per pluginlibstardust_<name>.solibstardust_<name>.dylibstardust_<name>.dll

Keep these files together, in the directory of the core library, and use them only with the release they came with. The core library looks for plugin libraries only in its own directory, the first time a call needs the plugin list. Opening a database, listing plugins and checking a license can trigger this scan. The bindings need no separate discovery setting.

Verification

The release build records each finished plugin library's SHA-256 in the core library. The core library accepts only the plugin libraries it was built with:

  1. It carries a SHA-256 for the plugin named by the file.
  2. The file's SHA-256 equals that value.
  3. The plugin library is from the same build and exports the plugin entry under its own name.

The core library reads the file once, checks its SHA-256, and loads the bytes that it checked. On Linux it loads the file through its open descriptor. On Windows it keeps the file open without write sharing until it loads. On macOS it keeps the file open and relies on the library's code signature.

Check the release download with minisign, as described in Verify the download. Each binding checks the core library's SHA-256. The core then checks its plugin libraries. See Load the core for the binding check. These checks detect a mismatched, corrupted, or replaced plugin library.

Available and unavailable plugins

A plugin library is available only after it passes every check. Missing libraries and failed checks make the plugin unavailable. Selecting an unavailable plugin causes database open to fail.

The plugins operation lists each available plugin with its name, description, version, requirements, willNotWorkWith, definition types, hooks, index providers, and database hooks. After them, it lists each unavailable plugin library as an object with name, file, and unavailable, which states the reason:

ReasonCause
This core library was not built with the plugin libraryThe file is not part of this release
The plugin library differs from the one this core library was built withThe file changed after the release was built
The plugin library cannot be loadedThe platform loader refused the file
The plugin library has no plugin entryThe file is not a Stardust plugin library
The plugin library is from another buildThe file came with a different release
The plugin library's name differs from its plugin nameThe file was renamed
A plugin with this name is already includedThe core library already contains that plugin
The plugin library's plugin is invalidThe plugin description is not usable

Development builds

Contributors who build the library from source can compile a development core library that loads plugin libraries without checking their SHA-256. This switch exists only at compile time, for development. Release packaging refuses a library built with it. Release libraries always check their plugin libraries.

Plugins compiled into the core

A plugin compiled into the core library is part of that library file. It needs no plugin library file or SHA-256 check, and it is always available to databases. The plugins that a core library contains are chosen when it is built, so changing them requires rebuilding the core library.

Go, Rust, Odin and C programs can link the static archive (libstardust_full.a, or stardust_full.lib on Windows) instead of the shared core library. Linking gives the commands. The archive contains the core and every plugin of its release, compiled in. The plugin set is fixed when you link your program. The backups plugin in the archive carries its own Zstandard copy under stardust_-prefixed symbol names, so your program can also link its own libzstd.

  • Ship only your executable. A static program uses no core library, no plugin libraries and no SHA-256 checks.
  • A static program does not look for plugin libraries. Plugin library files beside the executable have no effect.
  • To use the plugins of another release, link your program again with that release's static archive.
  • Each database still enables plugins when it opens, as described in Enable plugins. The license plugins permission applies as it does for the shared library.

Check the download with minisign before you link the archive, as described in Verify the download. The bindings do not check the SHA-256 of a statically linked core: it is part of your executable.

Enable plugins

Use the plugins database option when opening a database. It works the same way for plugin libraries, plugins compiled into the core, and static links. Selection applies until the database closes. Only named plugins are enabled.

SelectionResult
Omit pluginsEnable no plugins
Empty object, {}Enable no plugins
Object, such as {reactors {}}Enable the named plugins with their load settings
An unknown nameThe open fails with invalid_argument

Each object key is a plugin name. Its value is a settings object. An empty settings object uses the plugin's defaults. Select only available plugins. Include each selected plugin's required dependencies in the object. The willNotWorkWith list names plugins that cannot be enabled together. If either plugin lists the other, opening the database fails with invalid_argument before configuration or storage changes. A plugin in this list can be absent from the library or disabled. Unknown settings and invalid values fail before the database opens. The adaptive planner accepts memoryShare. The history plugin accepts scheduling settings, backups accepts its targets, and SDK generation accepts language output settings. The reactors and objects plugins accept only empty settings. A disabled plugin does not install its database hooks, definition types, or index providers. A write that sets stardust/history reports unavailable when the database does not enable history. A write that sets stardust/object/digest reports unavailable when the database does not enable objects.

Plugin operations

A plugin serves named operations through two calls:

CallInputResult
plugin_callRequest documentResult document
plugin_streamRequest documentByte stream, such as an object upload

Bindings provide typed wrappers for the supported operations. Each wrapper builds a request and calls the same underlying operation. A disabled plugin reports unavailable. An unknown operation reports invalid_argument with code unknown_operation. Plugins name their runtime description status and their wait for committed work sync, when they offer them.

Plugin boundary

All plugins are first-party plugins from the Stardust project. The plugin entry that a plugin library exports is internal to Stardust: it is not part of the C ABI, and the bindings do not call it. There is no interface for third-party plugins.

Operations

OperationReturnsSummary
Database.PluginCalldocumentdatabase.plugin_call(plugin, operation, request) runs a named operation of an enabled plugin and returns its owned result document.
Database.PluginStreamplugin_streamdatabase.plugin_stream(plugin, operation, request) opens an owned plugin stream of a named stream operation.
PluginStream.Writecountplugin_stream.write(data) appends bytes to the stream and returns the count accepted.
PluginStream.Readtextplugin_stream.read(length) returns up to length bytes from the stream's position and advances it.
PluginStream.Seekintegerplugin_stream.seek(offset, whence) moves the stream's position and returns it.
PluginStream.Finishdocumentplugin_stream.finish() ends the stream and returns its owned result document.
PluginStream.ClosenoneRelease the plugin_stream handle, ending an unfinished stream without finishing it.

Database.PluginCall

go93 B
func (d *Database) PluginCall(plugin, operation string, request *Document) (*Document, error)

Parameters

ParameterTypeOwnershipDefaultMeaning
pluginstringValueRequiredThe enabled plugin's name.
operationstringValueRequiredThe plugin's call operation.
request*DocumentBorrowed for the callAbsentThe borrowed request document. Its absence reads as an empty object.

Errors

StatusWhen
StatusInvalidArgumenta name is empty, the plugin has no call operation with the name, or the plugin refuses the request
StatusUnavailablethe plugin is unknown or the database does not enable it
StatusNotFoundthe plugin reports that the request names nothing it holds
StatusConflictthe plugin's write lost to a concurrent change, or the database holds writes
StatusEvaluationFailedthe plugin cannot read its stored state
StatusFailedthe plugin's work failed
StatusTimeoutthe plugin's wait passed its limit
StatusClosedthe database is closed
StatusOutOfMemorynative memory is exhausted

Notes

The plugin decodes the request and builds the result. A result is null when the operation returns nothing. Returned documents omit empty object and array members recursively; empty roots, array elements and scalar values remain. Each plugin documents its operations: object store, reactors and SDK generation.

Examples

Wait for live mutations, then describe them

go457 B
options, err := stardust.Parse([]byte(`{maxWait {#dur PT5S}}`), stardust.DUST)
if err != nil {
	return err
}
defer options.Close()
// sync returns a null document; db.Reactors().Sync wraps the same call.
synced, err := db.PluginCall("reactors", "sync", options)
if err != nil {
	return err
}
synced.Close()

// A nil request reads as an empty object.
result, err := db.PluginCall("reactors", "status", nil)
if err != nil {
	return err
}
defer result.Close()
TRON132 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
044C 68656164txt "head"
0902 04000000000000 00i64 4
120F 0A 04000000 0900 0000map leaf, size 10: @4 → @9
1C6C 637572736F72txt "cursor"
2302 04000000000000 00i64 4
2C0F 0A 1C000000 2300 0000map leaf, size 10: @1C → @23
3607 0E 04200000 1200 0000 2C000000map branch, size 14, depth 1, slots 2, 13: @12 → @2C
447C 656E61626C6564txt "enabled"
4C09bit true
4D0F 0A 44000000 4C00 0000map leaf, size 10: @44 → @4C
577C 70656E64696E67txt "pending"
5F01bit false
600F 0A 57000000 5F00 0000map leaf, size 10: @57 → @5F
6A07 12 28020000 3600 0000 4D000000 6000 0000map branch, size 18, depth 0 (root), slots 3, 5, 9: @36 → @4D → @60

C exports: stardust_db_plugin_call, stardust_result_take_document, stardust_result_error, stardust_result_release

Database.PluginStream

go99 B
func (d *Database) PluginStream(plugin, operation string, request *Document) (*PluginStream, error)

Parameters

ParameterTypeOwnershipDefaultMeaning
pluginstringValueRequiredThe enabled plugin's name.
operationstringValueRequiredThe plugin's stream operation.
request*DocumentBorrowed for the callAbsentThe borrowed request document. Its absence reads as an empty object.

Errors

StatusWhen
StatusInvalidArgumenta name is empty, the plugin has no stream operation with the name, or the plugin refuses the request
StatusUnavailablethe plugin is unknown or the database does not enable it
StatusNotFoundthe plugin reports that the request names nothing it holds
StatusConflictthe database holds writes
StatusEvaluationFailedthe plugin cannot read its stored state
StatusClosedthe database is closed
StatusOutOfMemorynative memory is exhausted

Notes

A stream supports the steps its operation documents. Each plugin documents its stream operations: object store.

Examples

Upload a payload and publish it

go407 B
upload, err := db.PluginStream("objects", "upload", nil)
if err != nil {
	return err
}
// Close ends the stream; the upload is abandoned unless it finished.
defer upload.Close()

if _, err := upload.Write([]byte("Hello, ")); err != nil {
	return err
}
if _, err := upload.Write([]byte("objects")); err != nil {
	return err
}

result, err := upload.Finish()
if err != nil {
	return err
}
defer result.Close()
TRON189 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
047C 23736861323536txt "#sha256"
0C14 40 393565383464 6133386362656664 6633663966366566 3032323031303734 6337333132346637 6366323032356433 6531643639653539 3161343130383536 6662txt "95e84da38cbefdf3f9f6ef02…"
4E0F 0A 04000000 0C00 0000map leaf, size 10: @4 → @C
584C 73697A65txt "size"
5D02 0E000000000000 00i64 14
660F 0A 58000000 5D00 0000map leaf, size 10: @58 → @5D
706C 646967657374txt "digest"
770F 0A 70000000 4E00 0000map leaf, size 10: @70 → @4E
81CC 64656475706C69 6361746564txt "deduplicated"
8E01bit false
8F0F 0A 81000000 8E00 0000map leaf, size 10: @81 → @8E
9907 0E 00440000 7700 0000 8F000000map branch, size 14, depth 1, slots 10, 14: @77 → @8F
A707 0E 00030000 6600 0000 99000000map branch, size 14, depth 0 (root), slots 8, 9: @66 → @99

C exports: stardust_db_plugin_stream_open, stardust_result_error, stardust_result_release

PluginStream.Write

go54 B
func (s *PluginStream) Write(data []byte) (int, error)

Requires exclusive use of the receiver handle.

Parameters

ParameterTypeOwnershipDefaultMeaning
data[]byteValueRequiredThe bytes to append, borrowed for the call.

Errors

StatusWhen
StatusInvalidArgumentthe stream has finished or does not accept writes
StatusConflictthe database holds writes while the plugin stores the bytes
StatusClosedthe plugin_stream is closed

Notes

A failure also reports the count accepted before it, returned together with the error. Those bytes stay part of the stream; continue with the remaining bytes.

Examples

Append bytes to an upload

go198 B
upload, err := db.Objects().Upload()
if err != nil {
	return err
}
defer upload.Close()

written, err := upload.Write([]byte("Hello, objects"))
if err != nil {
	return err
}
result := int64(written)
TRON21 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
0402 0E000000000000 00i64 14

C exports: stardust_db_plugin_stream_write

PluginStream.Read

go50 B
func (s *PluginStream) Read(p []byte) (int, error)

Requires exclusive use of the receiver handle.

Parameters

ParameterTypeOwnershipDefaultMeaning
p[]byteValueRequiredThe most bytes to read.

Errors

StatusWhen
StatusInvalidArgumentthe stream does not support reads, or length is negative
StatusEvaluationFailedstored bytes do not match their digest
StatusClosedthe plugin_stream is closed

Notes

A read is shorter than length only at the end, and empty at or past it.

Examples

Read the start of a payload

go598 B
upload, err := db.Objects().Upload()
if err != nil {
	return err
}
defer upload.Close()
if _, err := upload.Write([]byte("Hello, objects")); err != nil {
	return err
}
published, err := upload.Finish()
if err != nil {
	return err
}
defer published.Close()

reader, err := db.Objects().Open("95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb")
if err != nil {
	return err
}
defer reader.Close()
// Read implements io.Reader: it reports io.EOF at the end.
data := make([]byte, 5)
read, err := reader.Read(data)
if err != nil {
	return err
}
// data holds "Hello".
result := int64(read)
TRON21 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
0402 05000000000000 00i64 5

C exports: stardust_db_plugin_stream_read

PluginStream.Seek

go68 B
func (s *PluginStream) Seek(offset int64, whence int) (int64, error)

Requires exclusive use of the receiver handle.

Parameters

ParameterTypeOwnershipDefaultMeaning
offsetint64ValueRequiredThe distance from whence, negative toward the start.
whenceintValueRequired0 for the start, 1 for the current position, 2 for the end.

Errors

StatusWhen
StatusInvalidArgumentthe stream does not support seeks, whence is unknown, or the position is negative or past the i64 range
StatusClosedthe plugin_stream is closed

Notes

whence 0 counts offset from the start, 1 from the current position and 2 from the end. A refused seek keeps the position.

Examples

Seek from the end of a payload

go549 B
upload, err := db.Objects().Upload()
if err != nil {
	return err
}
defer upload.Close()
if _, err := upload.Write([]byte("Hello, objects")); err != nil {
	return err
}
published, err := upload.Finish()
if err != nil {
	return err
}
defer published.Close()

reader, err := db.Objects().Open("95e84da38cbefdf3f9f6ef02201074c73124f7cf2025d3e1d69e591a410856fb")
if err != nil {
	return err
}
defer reader.Close()
// io.SeekEnd counts from the end, so -7 is the start of "objects".
result, err := reader.Seek(-7, io.SeekEnd)
if err != nil {
	return err
}
TRON21 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
0402 07000000000000 00i64 7

C exports: stardust_db_plugin_stream_seek

PluginStream.Finish

go50 B
func (s *PluginStream) Finish() (*Document, error)

Requires exclusive use of the receiver handle.

Errors

StatusWhen
StatusInvalidArgumentthe stream has finished or has no finish step
StatusConflictthe database holds writes
StatusClosedthe plugin_stream is closed
StatusOutOfMemorynative memory is exhausted

Notes

A failed finish keeps the stream, so finish can be retried. Close the stream separately.

Examples

Publish the same payload twice

go428 B
// The second finish reports deduplicated true and stores nothing new.
var result *stardust.Document
for range 2 {
	upload, err := db.Objects().Upload()
	if err != nil {
		return err
	}
	if _, err := upload.Write([]byte("Hello, objects")); err != nil {
		upload.Close()
		return err
	}
	if result != nil {
		result.Close()
	}
	result, err = upload.Finish()
	upload.Close()
	if err != nil {
		return err
	}
}
defer result.Close()
TRON189 B
Offset (hex)Bytes (hex)Meaning
0054 52 4F 4Emagic TRON
047C 23736861323536txt "#sha256"
0C14 40 393565383464 6133386362656664 6633663966366566 3032323031303734 6337333132346637 6366323032356433 6531643639653539 3161343130383536 6662txt "95e84da38cbefdf3f9f6ef02…"
4E0F 0A 04000000 0C00 0000map leaf, size 10: @4 → @C
584C 73697A65txt "size"
5D02 0E000000000000 00i64 14
660F 0A 58000000 5D00 0000map leaf, size 10: @58 → @5D
706C 646967657374txt "digest"
770F 0A 70000000 4E00 0000map leaf, size 10: @70 → @4E
81CC 64656475706C69 6361746564txt "deduplicated"
8E09bit true
8F0F 0A 81000000 8E00 0000map leaf, size 10: @81 → @8E
9907 0E 00440000 7700 0000 8F000000map branch, size 14, depth 1, slots 10, 14: @77 → @8F
A707 0E 00030000 6600 0000 99000000map branch, size 14, depth 0 (root), slots 8, 9: @66 → @99

C exports: stardust_db_plugin_stream_finish, stardust_result_take_document, stardust_result_error, stardust_result_release

PluginStream.Close

go30 B
func (s *PluginStream) Close()

Consumes the receiver handle.

Notes

Close waits only for a call already in progress on the stream; start no other call once close begins. It is valid after the database closes. A plugin documents what close abandons; the object store reclaims an upload stage at the next open when close could not abandon it.

Examples

Abandon an unfinished upload

go212 B
upload, err := db.Objects().Upload()
if err != nil {
	return err
}
if _, err := upload.Write([]byte("draft")); err != nil {
	upload.Close()
	return err
}
// Closing before finish publishes nothing.
upload.Close()

C exports: stardust_db_plugin_stream_release