Skip to content

Instantly share code, notes, and snippets.

@JJTech0130
Last active October 23, 2025 01:50
Show Gist options
  • Select an option

  • Save JJTech0130/1bbfde1d52bfd60e023c7d21ba0b5b05 to your computer and use it in GitHub Desktop.

Select an option

Save JJTech0130/1bbfde1d52bfd60e023c7d21ba0b5b05 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
set -euo pipefail
# Configuration
CONFIG_ID="8da72a14ba6942ac904c2f028aada7cb" # Simlessly eSIM config ID
CBRS_PREFIX="315010" # CBRS MCC+MNC per your request
CBRS_IBN="9999" # CBRS IBN for testing/shared
DEFAULT_KI="$(printf 'A%.0s' {1..32})" # 32 hex chars of 'A' -> 16 bytes (testing)
DEFAULT_OPC="$(printf 'A%.0s' {1..32})" # same for OPc
# If you don't have open5gs-dbctl, you can install it:
# sudo curl https://raw.githubusercontent.com/open5gs/open5gs/refs/heads/main/misc/db/open5gs-dbctl -o /usr/local/bin/open5gs-dbctl && sudo chmod +x /usr/local/bin/open5gs-dbctl
OPEN5GS_CMD="open5gs-dbctl" # must be in PATH
SIMLESSLY_BASE="https://rsp.simlessly.com/api/v1/fleet"
TIMEZONE="America/New_York"
# Helpers
usage() {
cat <<EOF
Usage: $0 <5-digit-subscriber-id> [--execute] [--token <simlessly-token>]
<5-digit-subscriber-id> : numeric, up to 5 digits (e.g. 1, 00001, 12345)
--token <token> : Simlessly authorization token (or set SIMLESSLY_TOKEN env)
EOF
exit 1
}
# ====== HELPERS =====
# Attribution: https://ethertubes.com/bash-luhn/
# Returns Luhn checksum for supplied sequence
luhn_checksum() {
sequence="$1"
sequence="${sequence//[^0-9]}" # numbers only plz
checksum=0
table=(0 2 4 6 8 1 3 5 7 9)
# Quicker to work with even number of digits
# prepend a "0" to sequence if uneven
i=${#sequence}
if [ $(($i % 2)) -ne 0 ]; then
sequence="0$sequence"
((++i))
fi
while [ $i -ne 0 ];
do
# sum up the individual digits, do extra stuff w/every other digit
checksum="$(($checksum + ${sequence:$((i - 1)):1}))" # Last digit
# for every other digit, double the value before adding the digit
# if the doubled value is over 9, subtract 9
checksum="$(($checksum + ${table[${sequence:$((i - 2)):1}]}))" # Second to last digit
i=$((i - 2))
done
checksum="$(($checksum % 10))" # mod 10 the sum to get single digit checksum
echo "$checksum"
}
# Returns Luhn check digit for supplied sequence
luhn_checkdigit() {
check_digit=$(luhn_checksum "${1}0")
if [ $check_digit -ne 0 ]; then
check_digit=$((10 - $check_digit))
fi
echo "$check_digit"
}
gen_uuid() {
cat /proc/sys/kernel/random/uuid
}
ms_timestamp() {
date +%s%3N
}
# ====== PARSE ARGS =====
if [ $# -lt 1 ]; then
usage
fi
SUBSCR_ID_RAW="$1"
shift || true
SIMLESSLY_TOKEN="${SIMLESSLY_TOKEN:-}"
while (( "$#" )); do
case "$1" in
--token) SIMLESSLY_TOKEN="$2"; shift 2 ;;
-h|--help) usage ;;
*) echo "Unknown arg: $1"; usage ;;
esac
done
if [ -z "$SIMLESSLY_TOKEN" ]; then
echo "Warning: Simlessly token not provided. You can pass --token or set SIMLESSLY_TOKEN env var."
fi
# Validate subscriber id numeric and <=5 digits
if ! [[ "$SUBSCR_ID_RAW" =~ ^[0-9]+$ ]] || [ "${#SUBSCR_ID_RAW}" -gt 5 ]; then
echo "Error: subscriber id must be numeric and up to 5 digits (e.g. 1 or 00001 or 12345)."
exit 2
fi
SUBSCR_ID="$(printf "%05d" "${SUBSCR_ID_RAW#0}")"
IMSI="${CBRS_PREFIX}${CBRS_IBN}${SUBSCR_ID}"
# Sanity check IMSI length 15
if [ "${#IMSI}" -ne 15 ]; then
echo "Internal error: IMSI length is ${#IMSI}, expected 15. IMSI='$IMSI'"
exit 3
fi
# Build ICCID: 89 (ITU) 01 (USA) + IMSI + checkdigit
ICCID_BASE="8901${IMSI}"
CHECKDIGIT="$(luhn_checkdigit "${ICCID_BASE}")"
ICCID="${ICCID_BASE}${CHECKDIGIT}"
# Use the hardcoded Ki and OPc (testing)
KI="${DEFAULT_KI}"
OPC="${DEFAULT_OPC}"
# ===== GENERATE ESIM =====
curl_json() {
local url="$1"; shift
curl -sS --location "$url" \
--header "simlessly-requestid: $(gen_uuid)" \
--header "simlessly-timestamp: $(ms_timestamp)" \
--header "simlessly-timezone: $TIMEZONE" \
--header "simlessly-token: $SIMLESSLY_TOKEN" \
--header "Content-Type: application/json" \
"$@"
}
# List existing eSIMs
echo "Finding existing eSIMs..."
LIST_JSON="$(curl_json "${SIMLESSLY_BASE}/list" --data '{}')"
echo "$LIST_JSON" | jq '.obj.pageData[]? | {id, iccid, imsi, status, createTime}' || true
ESIM_IDS=($(echo "$LIST_JSON" | jq -r '.obj.pageData[]?.id'))
if ((${#ESIM_IDS[@]})); then
echo "Found ${#ESIM_IDS[@]} existing eSIM(s): ${ESIM_IDS[*]}"
else
echo "No existing eSIMs found."
fi
# Delete existing eSIMs if any
if ((${#ESIM_IDS[@]})); then
echo "Deleting existing eSIMs..."
IDS_JSON=$(printf '%s\n' "${ESIM_IDS[@]}" | jq -R . | jq -s '{ids:.}')
DELETE_RESP="$(curl_json "${SIMLESSLY_BASE}/delete" --data "$IDS_JSON")"
echo "$DELETE_RESP" | jq .
echo "Deletion done."
fi
# --- Step 3: Generate new eSIM ---
echo "Generating new eSIM with IMSI: $IMSI, ICCID: $ICCID"
GEN_PAYLOAD=$(jq -n \
--arg iccid "$ICCID" \
--arg imsi "$IMSI" \
--arg ki "$KI" \
--arg opc "$OPC" \
--arg configId "$CONFIG_ID" \
'{iccid:$iccid, imsi:$imsi, ki:$ki, opc:$opc, configId:$configId, acFormats:["STRING","PICTURE","AC_LINK"]}'
)
GEN_RESP="$(curl_json "${SIMLESSLY_BASE}/single-generate" --data "$GEN_PAYLOAD")"
echo "$GEN_RESP" | jq .
if [[ "$(echo "$GEN_RESP" | jq -r '.success')" != "true" ]]; then
echo "Error: single-generate failed"
exit 2
fi
echo "eSIM generation done."
# Add the IMSI to Open5GS DB
$OPEN5GS_CMD add "${IMSI}" "${KI}" "${OPC}"
echo "Added IMSI ${IMSI} to Open5GS database."
# We will need to wait for AC generation; poll list endpoint until AC is available
echo "Waiting for activation code generation..."
AC_VALUE=""
for attempt in {1..10}; do
sleep 3
echo "Polling attempt $attempt/10..."
LIST2_JSON="$(curl_json "${SIMLESSLY_BASE}/list" --data '{}')"
AC_VALUE="$(echo "$LIST2_JSON" | jq -r --arg iccid "$ICCID" '.obj.pageData[]? | select(.iccid==$iccid) | .ac // empty')"
if [[ -n "$AC_VALUE" ]]; then
echo "eSIM found after $attempt attempts."
ICCID_FOUND="$(echo "$LIST2_JSON" | jq -r --arg iccid "$ICCID" '.obj.pageData[]? | select(.iccid==$iccid) | .iccid')"
echo "ICCID: $ICCID_FOUND"
echo "Activation Code: $AC_VALUE"
echo
echo "QR Code (UTF-8):"
qrencode -t UTF8 "LPA:$AC_VALUE"
exit 0
fi
done
echo "Error: Activation code not found after polling."
exit 3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment