A bash exchange example
A Linkspace Exchange Tutorial
Available in the pkg or in repository/examples/
Available in the repo/examples/
See repo/dev/exchange.md for some open design issues/notes.
This is a rather dumb client & host. It simply forwards requests. It does no pruning before sending or receiving.
- anyhost client
#!/bin/bash
set -euo pipefail
export SERVER=${SERVER:-${1:-"127.0.0.1:5020"}}
export LK_GROUP=${LK_GROUP:-[#:test]}
socat /dev/null tcp:$SERVER
export LK_PASS=$(lk key --no-pubkey --no-enckey --display-pass)
echo Connecting $LK_GROUP $SERVER
socat tcp:$SERVER,keepalive exec:"anyhost.handshake.sh connect anyhost.client-io.sh",fdout=4
#websocat -E --binary ws://$SERVER sh-c:"handshake.sh connect client_io.sh"
#!/bin/bash
# parent should set out to fd4. otherwise add a exec 4>&1 1>&2
set -euo pipefail
function fin (){
kill -9 -- -$$ $(jobs -p) 2>/dev/null || true
echo Disconnected - $THEIR_KEY
}
trap "fin" EXIT
cd $SESSION
# ensure we have a fixed [b:...] repr (lns [#:name] could update between proc calls)
LK_GROUP=$(lk eval "$LK_GROUP" | lk encode b:32)
THEIR_KEY=$(lk eval "$THEIR_KEY" | lk encode b:32)
echo SESSION=$SESSION
echo THEIR_KEY=$THEIR_KEY
echo LK_GROUP=$LK_GROUP
lk link --create [u64:0] ":[#:0]:/rxlog/$THEIR_KEY" --write db
lk link --create [u64:0] ":[#:0]:/txlog/$THEIR_KEY" --write db
LAST_RX=$(lk --private watch --max 1 ":[#:0]:/rxlog/$THEIR_KEY" | lk pktf [create:str])
LAST_TX=$(lk --private watch --max 1 ":[#:0]:/txlog/$THEIR_KEY" | lk pktf [create:str])
lk eval "last rx [u64:$LAST_RX/us:str]\nlast tx [u64:$LAST_TX/us:str]\n"
lk set-status exchange $LK_GROUP process anyhost-client --data-str "$(lk e "OK\nPID:$$\nSESSION:$SESSION")" --data-repeat &
export LK_SKIP_HASH=true
# save reads from stdin, ie. the server
LK_SKIP_HASH=false lk save --new db --new stdout \
| lk pktf --inspect "RX [domain:str] [spacename:str] [hash:str]" \
| lk --private collect ":[#:0]:/rxlog/$THEIR_KEY" \
--min-interval 1m \
--forward null \
--write db &
# read the pull request made by other apps and place them into the group
lk --private watch --new-only "[f:exchange]:[#:0]:/pull/$LK_GROUP:**" \
| lk --private rewrite \
--group $LK_GROUP \
--write db --write stdout sign-all \
| lk p ">>>>new request [hash:str]\n[data]\n<<<<" &
# The exchange logic is to have every piece of data created locally send to a server
lk watch --bare --mode log-asc -- "group:=:$LK_GROUP" "hop:=:[u32:0]" "recv:>:[u64:$LAST_TX]" \
| lk get-links skip \
| lk dedup \
| lk pktf --inspect "[now:str] SENDING [hash:str]" \
| tee --output-error=exit >( cat >&4 ) \
| lk --private collect ":[#:0]:/txlog/$THEIR_KEY" \
--min-interval 1m \
--forward null \
--write db &
echo PIDS $(jobs -p)
wait -n
- handshake
#!/bin/bash
set -euo pipefail
if [[ ${SOCAT_PEERADDR+x} ]]
then
export THEIR_ADDR=$SOCAT_PEERADDR:$SOCAT_PEERPORT
else
# websocat
export THEIR_ADDR=${WEBSOCAT_CLIENT:-$SERVER}
fi
export SESSION=$(mktemp -dt $THEIR_ADDR.XXXXX)
MODE=${1:-serve}
lk handshake --max-diff-secs 6000 \
--write stdout --write file:$SESSION/handshake.out \
--forward file:$SESSION/handshake.in \
$MODE >&4
export THEIR_KEY=$(cat $SESSION/handshake.in | lk filter --bare --signed --max-new 1 | lk pktf "[pubkey/?:b]")
echo Connected $THEIR_ADDR - Their Key : $THEIR_KEY 1>&2
exec ${@:2}
- anyhost server
#!/bin/bash
set -euo pipefail
export PORT=${PORT:-"5020"}
echo My Key $(lk key)
echo Serving $LK_GROUP $PORT
export LK_PASS=$(lk key --no-pubkey --no-enckey --display-pass)
function fin (){
kill -9 -- -$$
kill -9 -- $(jobs -p) 2>/dev/null || true
echo Disconnected - $LK_GROUP $PORT
}
trap "fin" EXIT
lk set-status exchange $LK_GROUP process anyhost-client --data-str "$(lk e "OK\nPID:$$\nwe're hosting")" --data-repeat &
socat tcp-listen:$PORT,fork exec:"anyhost.handshake.sh serve anyhost.serve-io.sh",fdout=4 &
PIDS=$(jobs -p)
echo PIDS $PIDS
wait -- $PIDS
#websocat -e -E --binary --ping-timeout 15 --ping-interval 10 \
# ws-l:0.0.0.0:5020 sh-c:"strace -e 'trace=!all' handshake.sh serve serve_io.sh"
#!/bin/bash
# parent should set out to fd4. otherwise add a exec 4>&1 1>&2
set -euo pipefail
PID=$$
function fin (){
kill -9 -- -$$ $(jobs -p) 2>/dev/null || true
echo $PID Disconnected - $THEIR_KEY
}
trap "fin" EXIT
cd $SESSION
# ensure we have a fixed [b:...] repr (lns [#:name] could update between proc calls)
LK_GROUP=$(lk eval "$LK_GROUP" | lk encode b:32)
THEIR_KEY=$(lk eval "$THEIR_KEY" | lk encode b:32)
echo SESSION=$SESSION
echo THEIR_KEY=$THEIR_KEY
echo LK_GROUP=$LK_GROUP
echo PID=$PID
lk link --create [u64:0] ":[#:0]:/rxlog/$THEIR_KEY" --write db
lk link --create [u64:0] ":[#:0]:/txlog/$THEIR_KEY" --write db
LAST_RX=$(lk --private watch --max 1 ":[#:0]:/rxlog/$THEIR_KEY" | lk pktf [create:str])
LAST_TX=$(lk --private watch --max 1 ":[#:0]:/txlog/$THEIR_KEY" | lk pktf [create:str])
lk eval "last rx [u64:$LAST_RX/us:str]\nlast tx [u64:$LAST_TX/us:str]\n"
export LK_SKIP_HASH=true
# save reads from std. i.e. what the client is sending
LK_SKIP_HASH=false lk save --new db --new stdout \
--old file:>( lk pktf "$PID Ignored [hash:str] (old)" >&2 ) \
| lk pktf --inspect "$PID RX [domain:str] [spacename:str] [hash:str]" \
| lk --private collect ":[#:0]:/rxlog/$THEIR_KEY" \
--min-interval 1m \
--forward null \
--write db > /dev/null &
# Read new request keypoints and return their content
lk watch --new-only "[f:exchange]:$LK_GROUP:/pull/$LK_GROUP:**" -- "pubkey:=:$THEIR_KEY" \
| lk pktf --inspect ">>>>Pull req [hash:str]\n[data]\n<<<<$PID " \
| lk multi-watch \
| lk dedup \
| lk pktf --inspect "$PID Tx [hash:str]" >&4
echo PIDS $(jobs -p)
wait -n