<

A bash exchange example

A Linkspace Exchange Tutorial

Available in the pkg or in repository/linkspace/examples/

Available in the repo/linkspace/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 status set 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 status set 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

Created: 2023-11-05 Sun 09:20