- Install
bun.curl -fsSL https://bun.sh/install | bash - Install Rust.
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
- Install
dfx.sh -ci "$(curl -fsSL https://internetcomputer.org/install.sh)"
Alternatively, all tooling is provided by the Nix flake:
nix developCreate the .env.local file:
PRIVATE_KEY="${PRIVATE_KEY}"
PUBLIC_KEY="${PUBLIC_KEY}"
FRONTEND_URL="${FRONTEND_URL}"
SMTP_HOST="${SMTP_HOST}"
SMTP_PORT="${SMTP_PORT}"
SMTP_USER="${SMTP_USER}"
SMTP_PASS="${SMTP_PASS}"
SMTP_FROM="${SMTP_FROM}"
PORT="${PORT}"Generate the signing keys for email verification JWTs:
mkdir -p .local
openssl genpkey -algorithm ed25519 -out .local/sign.pem
openssl pkey -in .local/sign.pem -pubout -out .local/sign.pubNote: macOS LibreSSL does not support Ed25519. Use the Nix shell or install OpenSSL via Homebrew.
For minimal local dev (no SMTP needed), create .env.local with
just the public key:
printf 'PUBLIC_KEY="%s"\n' "$(cat .local/sign.pub)" > .env.localInstall dependencies:
bun iStart the local replica and deploy canisters:
./scripts/init-local.shAfter signing up and entering your email in the UI, generate a verification token:
bun run scripts/generate-verify-token.ts <your-email>Visit http://localhost:5173/verify?token=<output> in the same
browser where you are signed in.
New users start with Pending status. Activating via dfx is
sufficient to use the app locally -- email verification is optional:
dfx canister call backend list_user_profiles '(record {})'
dfx canister call backend update_user_profile '(record { user_id = "<id>"; status = opt variant { Active } })'Build the backend API library:
bun turbo -F @ssn/backend-api buildBuild the management canister library:
bun turbo -F @ssn/management-canister buildRun the frontend development server:
bun turbo -F frontend startCI runs lint and build/test jobs per service, skipping jobs when
their source paths have not changed. See the comments at the top of
.github/workflows/build-and-test.yml for the dependency graph. If
you add cross-canister dependencies or new packages, update the path
filters in the workflow files.
bun formatTo add a controller (make a user into an admin):
dfx canister update-settings --add-controller ${CONTROLLER_PRINCIPAL} backendTo remove a controller (remove admin rights from a user):
dfx canister update-settings --remove-controller ${CONTROLLER_PRINCIPAL} backendTo top up cycles for a canister, use the following command:
dfx wallet send ${CANISTER_ID} ${CYCLES_AMOUNT}To export a CSV of all canisters tracked by the Console:
bun -F scripts fetch-canisters-csvThe above script defaults to mainnet, to use a different network:
bun -F scripts fetch-canisters-csv --network="local"List the current subnet canister ranges in the canister:
dfx canister call --network test canister-history list_subnet_canister_ranges '(record {})'Get the current subnet canister ranges from the subnet:
bun -F scripts fetch-subnet-canister-rangesSet the subnet canister ranges in the canister:
dfx canister call --network test canister-history update_subnet_canister_ranges '(
record {
canister_ranges = vec {
record {
principal "${RANGE_START_PRINCIPAL}";
principal "${RANGE_END_PRINCIPAL}";
};
};
},
)'List the tracked canisters:
dfx canister call --network test canister-history list_subnet_canister_ids '(record {})'List changes for a specific canister:
dfx canister call --network test canister-history list_canister_changes '(
record {
reverse = null;
page = null;
canister_id = principal "${TARGET_CANISTER_PRINCIPAL}";
limit = opt (50 : nat64);
},
)'Encode the call args for the management canister:
./didc encode --defs ./src/management-canister/ic.did --types '(canister_info_args)' '(
record {
canister_id = principal "${TARGET_CANISTER_PRINCIPAL}";
num_requested_changes = opt 100;
}
)'Make the call to your cycles wallet:
dfx canister call --ic --candid ./cycles_wallet.did ${CYCLES_WALLET_PRINCIPAL} wallet_callEnter the following options into Candid assist:
hex:${ARGS_HEX}0canister_infoaaaaa-aa
Decode the output:
./didc decode --defs ./src/management-canister/ic.did --types '(canister_info_result)' ${RESULT_HEX}