Emery Hemingway e8501350c7 | ||
---|---|---|
src | ||
.envrc | ||
.gitignore | ||
README.md | ||
Tupfile | ||
Tuprules.jq | ||
Tuprules.tup | ||
assertions.prs | ||
base64.prs | ||
build-nim-sbom.nix | ||
config.prs | ||
default.nix | ||
file_system.prs | ||
inotify_actor.prs | ||
lock.json | ||
mountpoints.prs | ||
mpv.config-example.pr | ||
net_mapper.prs | ||
rofi.prs | ||
sbom.json | ||
sql.prs | ||
syndicate_utils.nimble |
README.md
Syndicate utils
Syndesizer
A Syndicate multitool that includes a number of different actors that become active via configuration.
Whether you use a single instance for many protocols or many specialized instances is up to you.
Cache
Observes patterns and reässert the values captured for a given lifetime. Takes the argument <cache { dataspace: #!any lifetime: float }>
. The lifetime of a cache counts down from moment a value is asserted.
Example configuration:
? <nixspace ?nixspace> [
; Require the nix_actor during observations.
?nixspace> ? <Observe <rec eval _> _> [
$config <require-service <daemon nix_actor>> ]
?nixspace> ? <Observe <rec realise _> _> [
$config <require-service <daemon nix_actor>> ]
; Cache anything captured by observers in the $nixspace for an hour.
; The nix_actor is not required during caching.
$config <require-service <daemon syndesizer>>
$config ? <service-object <daemon syndesizer> ?cap> [
$cap <cache { dataspace: $nixspace lifetime: 3600.0 }> ]
]
File System Usage
Summarize the size of file-system directory. Equivalent to du -s -b
.
Query the size of a directory in bytes by observing <file-system-usage "/SOME/PATH" ?size>
.
# Configuration example
? <exposed-dataspace ?ds> [
<require-service <daemon syndesizer>>
? <service-object <daemon syndesizer> ?cap> [
$cap <file-system-usage { dataspace: $ds }>
]
]
HTTP driver
Experimental HTTP server that services requests using some version of the http Syndicate protocol schema.
# Configuration example
let ?not-found = dataspace
$not-found ? <request _ ?res> [
$res ! <status 503 "Service unavailable">
$res ! <done "No binding here.">
]
let ?greeting = dataspace
$greeting ? <request _ ?res> [
$res ! <status 200 "ok">
$res ! <chunk "Hello world">
$res ! <done "!">
]
let ?http = dataspace
$http [
<http-bind #f 80 get [ ] $not-found>
<http-bind #f 80 get [|...|] $not-found>
<http-bind #f 80 get ["hello"] $greeting>
]
? <service-object <daemon http-driver> ?cap> [
$cap <http-driver { dataspace: $http }>
]
<daemon http-driver {
argv: [ "/bin/syndesizer" ]
clearEnv: #t
protocol: application/syndicate
}>
<require-service <daemon http-driver>>
JSON Socket Translator
Communicate with sockets that send and receive lines of JSON using <send …>
and <recv …>
messages.
Responds to the gatekeeper step <json-socket-translator { socket: <unix "…"> / <tcp "…" … }> $resolver>
.
# MPV configuration example
<require-service <daemon mpv-server>>
<daemon mpv-server {
argv: [
"/run/current-system/sw/bin/mpv"
"--really-quiet"
"--idle=yes"
"--no-audio-display"
"--input-ipc-server=/run/user/1000/mpv.sock"
"--volume=75"
]
protocol: none
}>
let ?resolver = dataspace
$resolver ? <accepted ?mpvSpace> $mpvSpace [
# announce the dataspace when the translator is connected
$config <mpv $mpvSpace>
$config <bind <ref { oid: "mpv" key: #x"" }> $mpvSpace #f>
# translate <play-file …> to an MPV command
?? <play-file ?file> [
! <send { "command": ["loadfile" $file "append-play"] }>
]
# clear the playlist on idle so it doesn't grow indefinitely
?? <recv {"event": "idle"}> [
! <send { "command": ["playlist-clear"] }>
]
]
? <service-state <daemon mpv-server> ready> [
<require-service <daemon syndesizer>>
? <service-object <daemon syndesizer> ?cap> [
$cap <resolve <json-socket-translator {
socket: <unix "/run/user/1000/mpv.sock">
}> $resolver>
]
]
JSON Stdio Translator
Executes a command, parses its JSON output, converts to record <recv @jsonData any>
, and publishes and messages it to a dataspace.
# Configuration example
<require-service <daemon syndesizer>>
let ?ds = dataspace
<bind <ref {oid: "syndicate" key: #x""}> $ds #f>
? <service-object <daemon syndesizer> ?cap> [
$cap <json-stdio-translator {
argv: [
"yt-dlp"
"--dump-json"
"https://youtu.be/RR9GkEXDvog"
]
dataspace: $ds
}>
]
Pulse proxy
An actor that produces proxies that accept assertions but only forwards them during a pulse window. This can be used to implement polling behavior or periodic service scheduling.
#!/usr/bin/env -S syndicate-server --config
let ?destination = dataspace
$destination ? ?x [
$log ! <log "destination" { +: $x }>
?- $log ! <log "destination" { -: $x }>
]
# Request a pulse service.
<q <service pulse {
label: "pulse-demo" # Not used by the pulse service, just here to match to the answer.
frequency: 4.0 # Four hertz pulse cycle.
duty-factor: 0.5 # Assertions forwarded half the time.
dither: 1.0 # Dither the cycle period to a standard deviation of 1 hertz.
target: $destination
}>>
? <a <service pulse _> <accepted ?target>> [
$target += <pulse-active>
]
# Start the pulse service on demand.
? <q <service pulse ?detail>> [
<require-service <daemon syndesizer>>
? <service-object <daemon syndesizer> ?obj> [
let ?rewriter = <* $config [<rewrite ?resp <a <service pulse $detail> $resp>>]>
$obj <resolve <pulse $detail> $rewriter>
]
<daemon syndesizer {
argv: "syndesizer"
clearEnv: #t
protocol: application/syndicate
}>
]
# Log if a service request is rejected.
? <a <service ?label ?detail> <rejected ?reason>> [
$log ! <log "service request rejected" { line: $reason label: $label detail: $detail }>
]
SQLite
Readonly access to SQLite databases. Asserts rows as records in response to SQL query assertions. Dynamic updates are not implemented.
Can be disabled by passing --define:withSqlite=no
to the Nim compiler.
# Configuration example
<require-service <daemon syndesizer>>
let ?sqlspace = dataspace
? <service-object <daemon syndesizer> ?cap> [
$cap <sqlite {
dataspace: $sqlspace
database: "/var/db/example.db"
}>
]
let ?tuplespace = dataspace
$sqlspace <query "SELECT id, name FROM stuff" $tuplespace>
$tuplespace [
? [?id ?name] [
$log ! <log "-" { row: <example-row $id $name> }>
]
? <sqlite-error ?msg ?ctx> [
$log ! <log "-" { msg: $msg ctx: $ctx }>
]
]
XML translator
Translates between Preserves and XML according to the Conventions for Common Data Types.
Examples:
<xml-translation "<foo a=\"1\"> <bar>hello world!</bar></foo>" <foo {"a": 1}<bar "hello world!">>>
<xml-translation "" [#t #f]>
<xml-translation "<<</>>" #f>
# Configuration example
? <sharedspace ?ds> [
$ds ? <Observe <rec xml-translation _> _> $config [
$config <require-service <daemon syndesizer>>
$config ? <service-object <daemon syndesizer> ?cap> [
$cap <xml-translator { dataspace: $ds }>
]
]
]
esc-printer-driver
A basic ESC/P printer driver.
Takes a path to a printer device file as a command line argument.
The driver speaks the gatekeeper protocol and responds to the <print {}>
step with a capability that prints strings it receives as messages.
While the <bold>
or <italic>
is asserted to this entity the printer will go into the corresponding font mode (if the printer supports it).
Sample Syndicate server script:
<require-service <daemon printer>>
let ?printer-resolver = dataspace
$printer-resolver ? <accepted ?printer> [
$printer <italic>
$printer ! "printer resolved\r\n"
]
? <service-object <daemon printer> ?cap> [
$cap <resolve <printer {}> $printer-resolver>
$log ! <log "-" { line: "printer started"}>
]
<daemon printer {
argv: [ "/bin/esc-printer-driver" "/dev/usb/lp0"]
protocol: application/syndicate
clearEnv: #t
}>
http-client
The inverse of http-driver
.
Caveats
- HTTPS is assumed unless the request is to port 80.
- If the request or response sets
Content-Type
toapplication/json
or…/preserves
the body will be a parsed Preserves value. - No caching or proxying.
- Internal errors propagate using a
400 Internal client error
response.
Sample Syndicate server script:
#!/usr/bin/env -S syndicate-server --control --config
# A dataspace for handling the HTTP response.
let ?response-handler = dataspace
$response-handler [
?? <done { "code": "EUR" "exchange_middle": ?middle } > [
$log ! <log "-" { line: <exchange EUR RSD $middle> }>
$control <exit 0>
]
]
# A dataspace for collecting a dataspace from the http-client.
let ?client-resolver = dataspace
$client-resolver ? <accepted ?client> $client [
<request
# Request Dinar to Euro exchange rate.
<http-request 0 "kurs.resenje.org" 443
get ["api" "v1" "currencies" "eur" "rates" "today"]
{content-type: "application/json"} {} #f
>
$response-handler
>
]
# Pass the resolver dataspace to the client.
? <service-object <daemon http-client> ?cap> [
$cap <resolve <http-client { response-content-type-override: "" }> $client-resolver>
]
<require-service <daemon http-client>>
<daemon http-client {
argv: [ "/bin/http-client" ]
clearEnv: #t
env: {
BUILD_SUM: $sum
}
protocol: application/syndicate
}>
mintsturdyref
A utility for minting Sturdyrefs.
mount_actor
Actor for mounting filesystems on Linux.
Sample Syndicate server script:
# Assert a file-system we want to mount.
<mount "/dev/sda3" "/boot" "vfat">
# Transform mount assertions into mount status observations.
? <mount ?source ?target ?fs> [
? <mount $source $target $fs _> [ ]
]
# Assert mounting succeded.
? <mount _ ?target _ #t> [
<service-state <mountpoint $target> ready>
]
# Assert mount failed.
? <mount _ ?target _ <failure _>> [
<service-state <mountpoint $target> failed>
]
# Assert the details into the machine dataspace.
? <machine-dataspace ?machine> [
$config ? <mount ?source ?target ?fs ?status> [
$machine <mount $source $target $fs $status>
]
]
# Require the mount_actor daemon.
<require-service <daemon mount_actor>>
<daemon mount_actor {
argv: ["/home/emery/src/bin/mount_actor"]
protocol: application/syndicate
}>
# Pass the daemon the config dataspace.
? <service-object <daemon mount_actor> ?cap> [
$cap { dataspace: $config }
]
msg
A utility that parses its command-line arguments as Preserves and send them as messages to $SYNDICATE_ROUTE
.
When called as assert
(by a symlink or a rename) it will make assertions instead.
PostgreSQL
Readonly access to PostgreSQL databases. Asserts rows as records in response to SQL query assertions. Dynamic updates are not implemented.
let ?postgreStep = <postgre {connection: [["host" "db.example.com"] ["dbname" "example"] ["user" "hackme"]]}>
let ?tuplespace = dataspace
$tuplespace ? ?row [
$log ! <log "-" { line: $row }>
]
let ?resolver = dataspace
$resolver ? <accepted ?sqlspace> [
$sqlspace ? <sql-error ?msg ?context> [
$log ! <log "-" { line: $msg context: $context }>
]
$sqlspace <query [SELECT firstname FROM users] $tuplespace>
]
<require-service <daemon postgre-actor>>
$config ? <service-object <daemon postgre-actor> ?cap> [
$cap <resolve $postgreStep $resolver>
]
<daemon postgre-actor {
argv: [ "/bin/postgre-actor" ]
clearEnv: #t
protocol: application/syndicate
}>
preserve_process_environment
This utility serializes it's process environment to Preserves and prints it to stdout. It can be used to feed the environment variables of a nested child of the Syndicate server back to the server. For example, to retreive the environmental variables that a desktop manager passed on to its children.
SQLite
Readonly access to SQLite databases. Asserts rows as records in response to SQL query assertions. Dynamic updates are not implemented.
# Configuration example
let ?sqliteStep = <sqlite { database: "/var/db/stuff.db" }>
let ?tuplespace = dataspace
$tuplespace ? ?row [
$log ! <log "-" { line: $row }>
]
let ?resolver = dataspace
$resolver [
? <rejected ?detail> [
$log ! <log "-" { line: $detail }>
]
? <accepted ?sqlspace> [
$log ! <log "-" { sqlspace: $sqlspace }>
$sqlspace ? <sql-error ?msg ?context> [
$log ! <log "-" { line: $msg context: $context }>
]
$sqlspace <query [ SELECT local_display_name FROM contacts ] $tuplespace>
]
]
<require-service <daemon sqlite-actor>>
$config ? <service-object <daemon sqlite-actor> ?cap> [
$cap <resolve $sqliteStep $resolver>
]
<daemon sqlite-actor {
argv: [ "/bin/sqlite-actor" ]
clearEnv: #t
protocol: application/syndicate
}>
syndump
Utility for printing assertions and messages. Parses the command-line arguments as a pattern, connects a dataspace via $SYNDICATE_ROUTE
, and writes observations to standard-output. Published assertions are prefixed by the +
character, retractions by -
, and messages by !
.
Example
# Print patterns in use, filter down with AWK to only the published patterns.
$ FS=':' syndump '<Observe ? _>' | awk -F : '/^+/ { print $2 }'
XSLT processor
Perform XML stylesheet transformations. For a given textual XSLT stylesheet and a textual XML document generate an abstract XML document in Preserves form. Inputs may be XML text or paths to XML files.
# Configuration example
let ?ds = dataspace
$ds [
? <xslt-transform "/stylesheet.xls" "/doc.xml" ?output> [
? <xml-translation ?text $output> [
$log ! <log "-" { xslt-output: $text }>
]
]
]
<require-service <daemon xslt_actor>>
? <service-object <daemon xslt_actor> ?cap> $cap [
<xml-translator { dataspace: $ds }>
<xslt { dataspace: $ds }>
]