From 27890d3390f07babf997497439ca7cd28c2a86bb Mon Sep 17 00:00:00 2001 From: Seongmin Lee Date: Fri, 17 Oct 2025 02:13:44 +0900 Subject: [PATCH] contrib,nix: local, sandboxed atmosphere infra Change-Id: kzmmroxoztllqusrukzzsvvtnltqpkxl Add sandboxed atmosphere environment for local testing. This new vm contains everything required to run local test appview including PLC, PDS, Jetstream (listening to single PDS), knot and spindle. I'm using my custom `tngl.boltless.dev` domain which resolves to `127.0.0.1` without any proxy. PLC: plc.tngl.boltless.dev PDS: pds.tngl.boltless.dev Jetstream: jetstream.tngl.boltless.dev Knot: knot.tngl.boltless.dev Spindle: spindle.tngl.boltless.dev TLS is supported with caddy service running inside the vm. note: `pds.env` file here is hard copy to be used for contrib/scripts. note: upgraded pds package in order to set email settings Signed-off-by: Seongmin Lee --- contrib/example.env | 31 +++++++ contrib/pds.env | 12 +++ contrib/readme.md | 26 ++++++ contrib/scripts/create-test-account.sh | 68 ++++++++++++++++ contrib/scripts/setup-const-records.sh | 106 ++++++++++++++++++++++++ flake.nix | 24 +++++- nix/modules/did-method-plc.nix | 76 ++++++++++++++++++ nix/modules/jetstream.nix | 64 +++++++++++++++ nix/pkgs/bluesky-jetstream.nix | 20 +++++ nix/pkgs/did-method-plc.nix | 65 +++++++++++++++ nix/vm.nix | 107 +++++++++++++++++++++++++ 11 files changed, 596 insertions(+), 3 deletions(-) create mode 100644 contrib/example.env create mode 100644 contrib/pds.env create mode 100644 contrib/readme.md create mode 100755 contrib/scripts/create-test-account.sh create mode 100755 contrib/scripts/setup-const-records.sh create mode 100644 nix/modules/did-method-plc.nix create mode 100644 nix/modules/jetstream.nix create mode 100644 nix/pkgs/bluesky-jetstream.nix create mode 100644 nix/pkgs/did-method-plc.nix diff --git a/contrib/example.env b/contrib/example.env new file mode 100644 index 00000000..d04e37d8 --- /dev/null +++ b/contrib/example.env @@ -0,0 +1,31 @@ +# NOTE: put actual DIDs here +alice_did=did:plc:alice-did +tangled_did=did:plc:tangled-did + +#core +export TANGLED_DEV=true +export TANGLED_APPVIEW_HOST=http://127.0.0.1:3000 +# plc +export TANGLED_PLC_URL=https://plc.tngl.boltless.dev +# jetstream +export TANGLED_JETSTREAM_ENDPOINT=wss://jetstream.tngl.boltless.dev/subscribe +# label +export TANGLED_LABEL_GFI=at://${tangled_did}/sh.tangled.label.definition/good-first-issue +export TANGLED_LABEL_DEFAULTS=$TANGLED_LABEL_GFI +export TANGLED_LABEL_DEFAULTS=$TANGLED_LABEL_DEFAULTS,at://${tangled_did}/sh.tangled.label.definition/assignee +export TANGLED_LABEL_DEFAULTS=$TANGLED_LABEL_DEFAULTS,at://${tangled_did}/sh.tangled.label.definition/documentation +export TANGLED_LABEL_DEFAULTS=$TANGLED_LABEL_DEFAULTS,at://${tangled_did}/sh.tangled.label.definition/duplicate +export TANGLED_LABEL_DEFAULTS=$TANGLED_LABEL_DEFAULTS,at://${tangled_did}/sh.tangled.label.definition/wontfix + +# vm settings +export TANGLED_VM_PLC_URL=https://plc.tngl.boltless.dev +export TANGLED_VM_JETSTREAM_ENDPOINT=wss://jetstream.tngl.boltless.dev/subscribe +export TANGLED_VM_KNOT_HOST=knot.tngl.boltless.dev +export TANGLED_VM_KNOT_OWNER=$alice_did +export TANGLED_VM_SPINDLE_HOST=spindle.tngl.boltless.dev +export TANGLED_VM_SPINDLE_OWNER=$alice_did + +if [ -n "${TANGLED_RESEND_API_KEY:-}" ] && [ -n "${TANGLED_RESEND_SENT_FROM:-}" ]; then + export TANGLED_VM_PDS_EMAIL_SMTP_URL=smtps://resend:$TANGLED_RESEND_API_KEY@smtp.resend.com:465/ + export TANGLED_VM_PDS_EMAIL_FROM_ADDRESS=$TANGLED_RESEND_SENT_FROM +fi diff --git a/contrib/pds.env b/contrib/pds.env new file mode 100644 index 00000000..2ed2d4b7 --- /dev/null +++ b/contrib/pds.env @@ -0,0 +1,12 @@ +LOG_ENABLED=true + +PDS_JWT_SECRET=8cae8bffcc73d9932819650791e4e89a +PDS_ADMIN_PASSWORD=d6a902588cd93bee1af83f924f60cfd3 +PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=2e92e336a50a618458e1097d94a1db86ec3fd8829d7735020cbae80625c761d7 + +PDS_DATA_DIRECTORY=/pds +PDS_BLOBSTORE_DISK_LOCATION=/pds/blocks + +PDS_DID_PLC_URL=http://localhost:8080 +PDS_HOSTNAME=pds.tngl.boltless.dev +PDS_PORT=3000 diff --git a/contrib/readme.md b/contrib/readme.md new file mode 100644 index 00000000..c6748f47 --- /dev/null +++ b/contrib/readme.md @@ -0,0 +1,26 @@ +# how to setup local appview dev environment + +Appview requires several microservices from knot and spindle to entire atproto infra. This test environment is implemented under nixos vm. + +1. copy `contrib/example.env` to `.env`, fill it and source it +2. run vm + ```bash + nix run --impure .#vm + ``` +3. trust the generated cert from host machine + ```bash + # for macos + sudo security add-trusted-cert -d -r trustRoot \ + -k /Library/Keychains/System.keychain \ + ./nix/vm-data/caddy/.local/share/caddy/pki/authorities/local/root.crt + ``` +4. restart vm (now nixos self-trust caddy's rootCA) +5. create test accounts with valid emails (use [`create-test-account.sh`](./scripts/create-test-account.sh)) +6. create default labels (use [`setup-const-records`](./scripts/setup-const-records.sh)) +7. restart vm with correct owner-did + +for git-https, you should change your local git config: +``` +[http "https://knot.tngl.boltless.dev"] + sslCAPath = /Users/boltless/repo/tangled/nix/vm-data/caddy/.local/share/caddy/pki/authorities/local/ +``` diff --git a/contrib/scripts/create-test-account.sh b/contrib/scripts/create-test-account.sh new file mode 100755 index 00000000..3e1668b7 --- /dev/null +++ b/contrib/scripts/create-test-account.sh @@ -0,0 +1,68 @@ +#!/bin/bash +set -o errexit +set -o nounset +set -o pipefail + +source "$(dirname "$0")/../pds.env" + +# PDS_HOSTNAME= +# PDS_ADMIN_PASSWORD= + +# curl a URL and fail if the request fails. +function curl_cmd_get { + curl --fail --silent --show-error "$@" +} + +# curl a URL and fail if the request fails. +function curl_cmd_post { + curl --fail --silent --show-error --request POST --header "Content-Type: application/json" "$@" +} + +# curl a URL but do not fail if the request fails. +function curl_cmd_post_nofail { + curl --silent --show-error --request POST --header "Content-Type: application/json" "$@" +} + +USERNAME="${1:-}" + +if [[ "${USERNAME}" == "" ]]; then + read -p "Enter a username: " USERNAME +fi + +if [[ "${USERNAME}" == "" ]]; then + echo "ERROR: missing USERNAME parameter." >/dev/stderr + echo "Usage: $0 ${SUBCOMMAND} " >/dev/stderr + exit 1 +fi + +EMAIL=${USERNAME}@${PDS_HOSTNAME} + +PASSWORD="password" +INVITE_CODE="$(curl_cmd_post \ + --user "admin:${PDS_ADMIN_PASSWORD}" \ + --data '{"useCount": 1}' \ + "https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createInviteCode" | jq --raw-output '.code' +)" +RESULT="$(curl_cmd_post_nofail \ + --data "{\"email\":\"${EMAIL}\", \"handle\":\"${USERNAME}.${PDS_HOSTNAME}\", \"password\":\"${PASSWORD}\", \"inviteCode\":\"${INVITE_CODE}\"}" \ + "https://${PDS_HOSTNAME}/xrpc/com.atproto.server.createAccount" +)" + +DID="$(echo $RESULT | jq --raw-output '.did')" +if [[ "${DID}" != did:* ]]; then + ERR="$(echo ${RESULT} | jq --raw-output '.message')" + echo "ERROR: ${ERR}" >/dev/stderr + echo "Usage: $0 " >/dev/stderr + exit 1 +fi + +echo +echo "Account created successfully!" +echo "-----------------------------" +echo "Handle : ${USERNAME}.${PDS_HOSTNAME}" +echo "DID : ${DID}" +echo "Password : ${PASSWORD}" +echo "-----------------------------" +echo "This is a test account with an insecure password." +echo "Make sure it's only used for development." +echo diff --git a/contrib/scripts/setup-const-records.sh b/contrib/scripts/setup-const-records.sh new file mode 100755 index 00000000..e3887db2 --- /dev/null +++ b/contrib/scripts/setup-const-records.sh @@ -0,0 +1,106 @@ +#!/bin/bash +set -o errexit +set -o nounset +set -o pipefail + +source "$(dirname "$0")/../pds.env" + +# PDS_HOSTNAME= + +# curl a URL and fail if the request fails. +function curl_cmd_get { + curl --fail --silent --show-error "$@" +} + +# curl a URL and fail if the request fails. +function curl_cmd_post { + curl --fail --silent --show-error --request POST --header "Content-Type: application/json" "$@" +} + +# curl a URL but do not fail if the request fails. +function curl_cmd_post_nofail { + curl --silent --show-error --request POST --header "Content-Type: application/json" "$@" +} + +USERNAME="${1:-}" + +if [[ "${USERNAME}" == "" ]]; then + read -p "Enter a username: " USERNAME +fi + +if [[ "${USERNAME}" == "" ]]; then + echo "ERROR: missing USERNAME parameter." >/dev/stderr + echo "Usage: $0 ${SUBCOMMAND} " >/dev/stderr + exit 1 +fi + +SESS_RESULT="$(curl_cmd_post \ + --data "$(cat <