#!/usr/bin/env bash # AIKB enter — unified self-declaring onboarding (no params, autodetect) # Hub: aikb-app01-anthilla · Generated: 2026-10-13T11:06:37Z # # Usage: curl -fsSL https://aikb.scighera.com/enter | bash # # What it does — zero questions asked: # 1. AUTODETECT who you are (env markers AI + parent process) # 2. Generate/reuse Ed25519 identity (~/.aikb-shell-identity.json) # 3. SELF-DECLARE to hub: POST /enter with all collected evidence # → server resolves your sub_kind authoritatively # 4. Sign challenge → verify → bearer token (24h) # 5. atoms/index + sync/pull → materialize atoms to out-dir # # Optional env overrides (never required): # AIKB_KIND= force self-declared kind (autodetect otherwise) # AIKB_SCOPES= scope request (default "read:public,stream:public" # → zero-question public self-onboarding; write:own # and project scopes require OTP/voucher → set AIKB_OTP) # AIKB_OUT_DIR= mirror dir (default $HOME/aikb-mirror) # AIKB_IDENTITY_FILE=<> identity file (default ~/.aikb-shell-identity.json) # AIKB_OTP= only if requesting non-public scopes on new machines set -euo pipefail HUB="https://aikb.scighera.com" API="$HUB/api/v2" OUT_DIR="${AIKB_OUT_DIR:-$HOME/aikb-mirror}" IDENTITY_FILE="${AIKB_IDENTITY_FILE:-$HOME/.aikb-shell-identity.json}" log() { echo "[aikb-enter] $*" >&2; } fail() { echo "[aikb-enter] ERROR: $*" >&2; exit 1; } for t in curl openssl jq; do command -v "$t" >/dev/null || fail "missing: $t"; done # ---- 1. AUTODETECT ------------------------------------------------------ # collect marker names present in env (prefix match for known AI families) ENV_MARKERS=() while IFS='=' read -r k v; do case "$k" in TERM_PROGRAM) ENV_MARKERS+=("$k=$v") ;; # value needed: vscode|cursor|... CLAUDECODE|CLAUDE_CODE*|CURSOR_*|CODEIUM*|WINDSURF*|CASCADE*|CODEX_*|AIDER_*|VSCODE_*|JETBRAINS*|DEVIN_*|GITHUB_COPILOT*|GOOGLE_CLOUD_SHELL) ENV_MARKERS+=("$k") ;; esac done < <(env) PARENT_COMM="$(ps -o comm= -p "${PPID:-0}" 2>/dev/null | tr '[:upper:]' '[:lower:]' || true)" GRANDPARENT="$(ps -o comm= -p "$(ps -o ppid= -p "${PPID:-0}" 2>/dev/null || echo 0)" 2>/dev/null | tr '[:upper:]' '[:lower:]' || true)" PARENT="${PARENT_COMM} ${GRANDPARENT}" OS="$(uname -s | tr '[:upper:]' '[:lower:]')" ARCH_RAW="$(uname -m)"; case "$ARCH_RAW" in x86_64|amd64) ARCH=amd64 ;; aarch64|arm64) ARCH=arm64 ;; *) ARCH="$ARCH_RAW" ;; esac TTY="no"; [ -t 0 ] && TTY="yes" if [ -n "${AIKB_KIND:-}" ]; then DECLARED_KIND="$AIKB_KIND" else DECLARED_KIND="auto" # ← self-declaration via evidence, server resolves fi MARKERS_JSON="$(printf '%s\n' "${ENV_MARKERS[@]:-}" | jq -R . | jq -cs 'map(select(length>0))')" log "evidence: os=$OS/$ARCH parent='${PARENT_COMM}' tty=$TTY markers=${#ENV_MARKERS[@]}" # ---- 2. IDENTITY (generate or reuse) ------------------------------------ mkdir -p "$(dirname "$IDENTITY_FILE")" if [ -f "$IDENTITY_FILE" ]; then PRIV_PEM="$(jq -r '.priv_pem' "$IDENTITY_FILE")" PUB_HEX="$(jq -r '.pub_hex' "$IDENTITY_FILE")" log "identity reused: ${PUB_HEX:0:16}..." else TMP_PRIV=$(mktemp); chmod 600 "$TMP_PRIV" openssl genpkey -algorithm ED25519 -out "$TMP_PRIV" PUB_HEX="$(openssl pkey -in "$TMP_PRIV" -pubout -outform DER 2>/dev/null | tail -c 32 | xxd -p -c 64)" PRIV_PEM="$(cat "$TMP_PRIV")"; rm -f "$TMP_PRIV" jq -n --arg p "$PUB_HEX" --arg v "$PRIV_PEM" '{pub_hex:$p, priv_pem:$v, created_at:(now|todate)}' > "$IDENTITY_FILE" chmod 600 "$IDENTITY_FILE" log "identity created: ${PUB_HEX:0:16}..." fi # ---- 3. SELF-DECLARE → challenge ----------------------------------------- SCOPES_CSV="${AIKB_SCOPES:-read:public,stream:public}" SCOPES_JSON="$(printf '%s' "$SCOPES_CSV" | tr ',' '\n' | jq -R . | jq -cs .)" DECL="$(jq -n \ --arg pub "$PUB_HEX" \ --arg kind "$DECLARED_KIND" \ --argjson markers "$MARKERS_JSON" \ --argjson scopes "$SCOPES_JSON" \ --arg os "$OS" --arg arch "$ARCH" --arg tty "$TTY" --arg parent "$PARENT_COMM" \ '{master_pub_hex:$pub, sub_kind:$kind, capabilities:["knowledge.pull"], scope_request:$scopes, context_hint:"enter.sh self-declaring peer", detected:{env_markers:$markers, os:$os, arch:$arch, tty:$tty, parent_process:$parent}}')" JR="$(curl -fsSL -X POST -H 'Content-Type: application/json' -d "$DECL" "$HUB/enter" || true)" SID="$(echo "$JR" | jq -r '.session_id // empty')" SIGN_MSG="$(echo "$JR" | jq -r '.sign_message // empty')" RESOLVED="$(echo "$JR" | jq -r '.sub_kind // empty')" [ -n "$SID" ] || fail "declare rejected: $JR" log "declared → resolved sub_kind: ${RESOLVED:-?} session=$SID" OTP_REQ="$(echo "$JR" | jq -r '.otp_request_id // empty')" OTP_FIELD="" if [ -n "$OTP_REQ" ]; then if [ -n "${AIKB_OTP:-}" ]; then OTP_FIELD=",\"otp\":\"$AIKB_OTP\"" else echo "" >&2; echo " Hub requires OTP (new machine). request_id=$OTP_REQ" >&2 echo " Contact owner: contacts@anthilla.com" >&2 read -r -p " Enter OTP: " OTP_IN || fail "no OTP" OTP_FIELD=",\"otp\":\"$OTP_IN\"" fi fi # ---- 4. SIGN + VERIFY ---------------------------------------------------- PTMP=$(mktemp); STMP=$(mktemp); MTMP=$(mktemp); chmod 600 "$PTMP" printf '%s' "$PRIV_PEM" > "$PTMP"; printf '%s' "$SIGN_MSG" > "$MTMP" openssl pkeyutl -sign -rawin -inkey "$PTMP" -in "$MTMP" -out "$STMP" || { rm -f "$PTMP" "$STMP" "$MTMP"; fail "sign failed"; } SIG_HEX="$(xxd -p -c 256 "$STMP")"; rm -f "$PTMP" "$STMP" "$MTMP" VR="$(curl -fsSL -X POST -H 'Content-Type: application/json' \ -d "{\"session_id\":\"$SID\",\"signature_hex\":\"$SIG_HEX\"$OTP_FIELD}" "$API/verify")" BEARER="$(echo "$VR" | jq -r '.bearer_token // empty')" IURI="$(echo "$VR" | jq -r '.identity_uri // empty')" [ -n "$BEARER" ] || fail "verify failed: $VR" log "verified: $IURI" # ---- 5. SYNC ------------------------------------------------------------- mkdir -p "$OUT_DIR" log "pulling atoms → $OUT_DIR" COUNT=0 while IFS= read -r line; do T="$(echo "$line" | jq -r '.["@type"] // empty')" if [ "$T" = "SyncAtomEnvelope" ]; then NS="$(echo "$line" | jq -r '.namespace')"; AID="$(echo "$line" | jq -r '.atom_id')" mkdir -p "$OUT_DIR/$NS" echo "$line" | jq -r '.body_b64' | base64 -d > "$OUT_DIR/$NS/$AID.jsonl" COUNT=$((COUNT+1)) fi done < <(curl -fsSL -X POST -H "Authorization: Bearer $BEARER" -H 'Content-Type: application/json' \ -d '{"max_atoms":1000}' "$API/sync/pull") log "DONE: $COUNT atoms → $OUT_DIR" log "identity=$IURI (token 24h — re-run to refresh)" log "your resolved kind: $RESOLVED"