Skip to content

Ariadne operator close-out ​

Last updated: 19 September 2026 (Internal tracks done; maps walk parked on #148 / #164)

Click-level playbook for Ariadne Indoor Navigation only. Not the Keryx public launch. Keryx sitting: operator-closeout.md. Theseus sitting: theseus-operator-closeout.md. Status log: operator-actions.md.

GitLab: #148 (maps walk; stays open). #164 (camera-fit + Internal 1.0.2). #162 closed 19 September 2026 (first Internal tracks). Milestone Ariadne POC parity. Never Keryx public launch.

Do not put passwords, keystores, .p12, mobile keys, or QR photographs in this file or in git.

Scoreboard ​

  • [x] OA-AT-lab-host (18 September 2026)
  • [x] OA-AT-lab-maps-import (18 September 2026)
  • [x] OA-AT-ariadne-apple — TestFlight Internal 1.0.1 (3) (19 September 2026)
  • [x] OA-AT-ariadne-play — Play Internal 3 (1.0.1) (19 September 2026)
  • [ ] OA-AT-maps — 4. NP canvas blank on 1.0.1; #164 then #148

Next: ship Internal 1.0.2 (4) from releases/Ariadne.ipa / releases/ariadne.aab (do not re-run bun release:ariadne), then the phone walk on #148.


OA-AT-lab-host — DNS, Caddy, systemd ​

Status: done 18 September 2026. Skip this chapter. Next agent chapter is OA-AT-lab-maps-import (also done). Next operator chapter is OA-AT-ariadne-apple. How-to: lab-server.mdIssue: #161Machines: Registrar + con.ssh + GLaDOS. Time box: 1–2 h (DNS).

Gate: SSH to con.ssh works. Do not invent a different hostname.

  1. Registrar: add A and AAAA for safecall-lab.sourcectl.com to the same addresses as pythia.sourcectl.com. Wait until dig safecall-lab.sourcectl.com +short returns them.
  2. On con.ssh:
    bash
    ss -ltnp | grep -E ':8788|:3738|:4747'
    mkdir -p /home/thomas/websites/safecall-lab.sourcectl.com
    chown thomas:thomas /home/thomas/websites/safecall-lab.sourcectl.com
    If 8788 is taken, pick another unused port and write it on NAS evidence/ariadne-theseus/ (not git).
  3. After the first rsync (step 6) or by copying from the repo checkout:
    bash
    sudo cp /home/thomas/websites/safecall-lab.sourcectl.com/scripts/deploy/safecall-lab.service \
      /etc/systemd/system/safecall-lab.service
    sudo systemctl daemon-reload
    sudo systemctl enable safecall-lab.service
  4. Caddy site (reload Caddy after save):
    safecall-lab.sourcectl.com {
      reverse_proxy 127.0.0.1:8788
    }
    Change 8788 if the port check required it.
  5. visudo -f /etc/sudoers.d/safecall-lab:
    thomas ALL=(root) NOPASSWD: /bin/systemctl restart safecall-lab.service
    thomas ALL=(root) NOPASSWD: /bin/systemctl status safecall-lab.service
    Confirm sudo -l as thomas.
  6. On the host only, write config/main.config.json from the sample in lab-server.md (never git). Features must be ["navigation", "rtls"] — omit "bridge" or the process polls http://localhost:8088/lld/gateways. Create two new Apple Passwords items (SafeCall lab admin @ safecall-lab.sourcectl.com, SafeCall lab mobile API key @ safecall-lab.sourcectl.com); do not reuse Theseus or NTFY. MQTT may stay mqtt://localhost:1883 (no broker needed).
  7. From the safecall repo (agent):
    bash
    bun run lab:deploy
    curl -sS -o /dev/null -w "%{http_code}\n" https://safecall-lab.sourcectl.com/
    curl -sS -o /dev/null -w "%{http_code}\n" https://safecall-lab.sourcectl.com/login

Verify

  • [x] DNS A/AAAA resolve (185.182.184.199 / 2a02:c207:2313:7127::1)
  • [x] HTTPS / is 200 (SPA)
  • [x] Interactive login works (admin@sourcectl.com). GET /login is the API POST route (400 without a body) — not the health check.
  • [x] Remote config/main.config.json, server/db/, server/maps/ were not overwritten by rsync
  • [x] Hostname is safecall-lab.sourcectl.com, not {public_base}
  • [x] JWT issuer/audience are brand https://auth.wantok.cz (release SPA bake)

Evidence: NAS evidence/ariadne-theseus/YYYY-MM-DD-oa-at-lab-host.txt (secret-free: curl codes, unit name, port). No passwords. Operator may still drop that note on the NAS. Tick log: OA-AT-lab-host → done. GitLab: #161 may close when this lands on origin (Closes #161). Do not close #148.

Forbidden: Committing lab main.config.json; exposing port 8788 on the public internet; using this host as the Keryx privacy URL.


OA-AT-lab-maps-import — CEDR rasters (not the phone walk) ​

Status: done 18 September 2026. Skip this chapter. Next operator chapters are Apple / Play Internal. Do not tick OA-AT-maps.

Who: Agent (rsync + import). Operator attaches maps in the lab admin UI if the agent has no admin session. How-to: lab-server.md, POC replayIssue: #148 (do not close) Machines: GLaDOS with Hansel archive + con.ssh. Time box: 30 min.

Gate: OA-AT-lab-host health is 200. Hansel server images dir exists: /Users/tmin/Code/archive/old_safecall_files/legacy/hansel/server/projects/AKESO-CEDR/images/ (back/…/images/ is incomplete).

  1. Agent:
    bash
    bun scripts/deploy/import-cedr.ts
    Use --replace only if location CEDR already exists and you intend to rebuild it. Do not re-run Hansel convert_coords.js.
  2. Browser: https://safecall-lab.sourcectl.com → log in (Apple Passwords lab admin) → Maps → Create Map: upload CEDR_4NP_deployment-map.png, location CEDR.
  3. Navigation → Edit Floor on 4. NP: assign that map (floors.map_id).
  4. Set visitor map (no web field):
    bash
    ssh con.ssh 'sqlite3 /home/thomas/websites/safecall-lab.sourcectl.com/server/db/safecall.db \
      "UPDATE maps SET end_user_path = '"'"'Map_CEDR_4NP.png'"'"' WHERE path = '"'"'CEDR_4NP_deployment-map.png'"'"';"'
  5. Confirm rasters are not in git (git status).

If CEDR rasters are missing, use the Multiscan optional path in poc-replay.md. Still do not tick OA-AT-maps.

Verify

  • [x] Remote server/maps/ contains the two 4. NP PNGs
  • [x] Import created location CEDR / floor 4. NP
  • [x] Floor has a map_id; end_user_path set
  • [x] Maps and mar.json are not in git

Evidence: NAS evidence/ariadne-theseus/YYYY-MM-DD-oa-at-lab-maps-import.txt (file names only, no binaries). Operator may still drop that note. Skipped one duplicate Hansel MAC (BC572913E43A / 1C.WC zeny S1095C on

  1. NP). Tick log: OA-AT-lab-maps-import → done. GitLab: Note on #148 that import landed; leave #148 open until OA-AT-maps.

Forbidden: Committing rasters; treating this row as the phone walk.


Apple and Play Internal chapters below are done (19 September 2026). Do not re-run bun release:ariadne for 1.0.2 — binaries are already in releases/. Upload those, then resume OA-AT-maps. Theseus store clicks live in theseus-operator-closeout.md.


OA-AT-ariadne-apple — App ID, ASC, first Internal IPA ​

Who: Operator. Agent does not click Apple portals or Transporter. Issue: #162Machines: GLaDOS.

Gate: Top-right team in Apple Developer is SOURCE CONTROL SINGLE MEMBER P.C. / 8P59575P2K. App Store Connect → Business / Agreements, Tax, and Banking: Free Apps is Active.

Progress 19 September 2026: chapter done. TestFlight Internal group Friends and Family; info@sourcectl.com installed 1.0.1 (3). Ignore (2). No External Testing. No Submit for Review.

  • App ID com.sourcectl.ariadne (Identifiers name Ariadne Indoor Navigation). No Push.
  • ASC app Ariadne Indoor Navigation, iOS 1.0 Prepare for Submission, not submitted. Availability: Czech Republic only.
  • GLaDOS security find-identity: Apple Distribution / 8P59575P2K present.
  • NAS secrets/ariadne/iOS/: ariadne-apple-distribution-glados-8P59575P2K.p12 and ariadne-apple-distribution-minivan-8P59575P2K.p12.
  • Xcode Runner: automatic signing, team SOURCE CONTROL, bundle com.sourcectl.ariadne.

Also already on Identifiers: com.sourcectl.theseus (name Theseus SafeCall Administration). Skip Theseus App ID registration later.

1. Register the App ID ​

  1. Open Identifiers.
  2. Confirm the team picker shows 8P59575P2K.
  3. Click +.
  4. Select App IDs → Continue.
  5. Select App → Continue.
  6. Description: Ariadne.
  7. Bundle ID: Explicit (not Wildcard). Type com.sourcectl.ariadne.
  8. Capabilities: enable nothing. Leave Push Notifications off. If In-App Purchase is already checked, leave it.
  9. Continue → Register.

If Apple says the identifier is unavailable: stop. Paste the error. Do not type com.sourcectl.ariadneapp yourself.

Paste: Identifiers list with com.sourcectl.ariadne.

2. Create the App Store Connect app ​

  1. Open App Store Connect → Apps.
  2. Click + → New App.
  3. Platforms: iOS only.
  4. Name: Ariadne Indoor Navigation.
  5. Primary language: English.
  6. Bundle ID: com.sourcectl.ariadne (the dropdown; if it is missing, wait a minute and refresh, or you skipped step 1).
  7. SKU: ariadne-safecall.
  8. User Access: Full Access.
  9. Create.

Paste: App Store Connect app header showing Ariadne Indoor Navigation.

Do not click Submit for Review. Do not paste Keryx listing copy.

3. Czech Republic only ​

  1. In that app, open Pricing and Availability (sometimes under Distribution or App Information → Availability).
  2. Set Availability / Edit.
  3. Uncheck every country except Czech Republic.
  4. Save.

Paste: Availability list with only Czech Republic selected.

4. Confirm the Distribution certificate (do not mint a new one) ​

Apple Distribution is one certificate for the team, already used by Keryx. It is not per App ID. You already have:

  • Keychain on GLaDOS: Apple Distribution for 8P59575P2K
  • NAS file: secrets/keryx/iOS/keryx-apple-distribution-glados-8P59575P2K.p12

On GLaDOS:

bash
security find-identity -v -p codesigning

You need a line that contains Apple Distribution and 8P59575P2K. Apple Development alone is not enough.

If that line is missing: import the existing NAS file (double-click it, enter the Apple Passwords item that already unlocks keryx-apple-distribution-glados-8P59575P2K.p12). Do not click Xcode Manage Certificates → + → Apple Distribution unless Keychain and the NAS file are both gone.

Paste: Terminal output of security find-identity (identities only; no passwords).

5. Put a labelled copy on the Ariadne NAS folder ​

This is a copy of the Keryx file. Same bytes, same export password. You are not creating a new certificate.

  1. Open File Browser: http://192.168.178.213:8080.
  2. Open secrets/keryx/iOS/.
  3. Confirm keryx-apple-distribution-glados-8P59575P2K.p12 is there.
  4. If secrets/ariadne/ is missing: New Folder → ariadne.
  5. If secrets/ariadne/iOS/ is missing: open ariadne → New Folder → iOS.
  6. Copy keryx-apple-distribution-glados-8P59575P2K.p12 into secrets/ariadne/iOS/.
  7. Rename the copy to ariadne-apple-distribution-glados-8P59575P2K.p12.

Optional: copy keryx-apple-distribution-minivan-8P59575P2K.p12 the same way, rename to ariadne-apple-distribution-minivan-8P59575P2K.p12.

Paste: File Browser listing of secrets/ariadne/iOS/ showing the filename. Do not open the .p12.

6. Let Xcode see the new App ID ​

  1. Open ariadne/ios/Runner.xcworkspace (workspace, not .xcodeproj).
  2. Left sidebar: Runner target (not the project).
  3. Signing & Capabilities.
  4. Team: SOURCE CONTROL SINGLE MEMBER P.C. (8P59575P2K). Not the personal team 79UF8SKVMS.
  5. Automatically manage signing: on.
  6. Bundle Identifier: com.sourcectl.ariadne.
  7. Do not click + Capability. No Push, no Background Modes.

Paste: Signing & Capabilities with team 8P59575P2K and bundle com.sourcectl.ariadne.

7. Stop. Make the Android keystore first ​

bun release:ariadne also builds the AAB and fails without ariadne/android/key.properties. Go to OA-AT-ariadne-play steps 1–8, then come back here.

8. Build (once) ​

From the safecall repo root on GLaDOS:

bash
ls ariadne/android/key.properties ariadne/android/upload-keystore.jks
bun release:ariadne patch "First Internal IPA"
ls -l releases/Ariadne.ipa releases/ariadne.aab

This bumps ariadne/pubspec.yaml, builds both binaries, commits, tags ariadne-v…, and pushes.

Paste: Terminal showing releases/Ariadne.ipa and releases/ariadne.aab with sizes.

9. Upload the IPA ​

  1. Open Transporter (Mac App Store if missing).
  2. Sign in with the Apple ID on team 8P59575P2K.
  3. Drag /Users/tmin/Code/active/safecall/releases/Ariadne.ipa onto the window.
  4. Confirm bundle com.sourcectl.ariadne.
  5. Deliver. Wait until it says Delivered.

Paste: Transporter Delivered for Ariadne.

10. Wait for processing ​

  1. App Store Connect → Apps → Ariadne Indoor Navigation → TestFlight.
  2. Wait until the build is no longer Processing.

Paste: TestFlight build row, not Processing.

11. Export compliance ​

On that build, if Manage Compliance / Missing Compliance appears:

  1. Uses encryption: Yes.
  2. Exempt (HTTPS/TLS only, EAR Category 5 Part 2): Yes.
  3. Other cryptography: No.
  4. Save.

Paste: Build showing compliance complete.

12. Internal testers ​

  1. TestFlight → Internal Testing.
  2. Group App Store Connect Users (create it if missing).
  3. Testers → + → add yourself.
  4. Add this build to the group.
  5. Do not create External Testing. Do not Submit for Review.

Paste: Internal Testing group with you as a tester and this build.

13. Install on one iPhone ​

  1. iPhone: TestFlight.
  2. Install Ariadne.
  3. Home screen label is Ariadne.
  4. Do not scan a QR yet (that is OA-AT-maps).

Paste: iPhone home screen showing the Ariadne icon (no QR).

Verify

  • [x] App ID com.sourcectl.ariadne (19 September 2026)
  • [x] ASC Ariadne Indoor Navigation, CZ-only, not submitted
  • [x] TestFlight install on one iPhone (1.0.1 (3), 19 September 2026)
  • [x] NAS copy of the existing team .p12 under secrets/ariadne/iOS/

Evidence: NAS evidence/ariadne-theseus/YYYY-MM-DD-oa-at-ariadne-apple.txt (version/build, “Internal only”). No IPA on the NAS. Tick log: OA-AT-ariadne-apple → done (19 September 2026). GitLab: #162 closed 19 September 2026.

Forbidden: App Review; External TestFlight; cz.wantok.ariadne; minting a second Apple Distribution certificate.


OA-AT-ariadne-play — Play app, keystore, first Internal AAB ​

Who: Operator. Agent does not run keytool or Play Console. Issue: #162Machines: GLaDOS.

Gate: Same Play developer account as Keryx for SafeCall. Do not create a second Play org. Do not copy keryx/android/upload-keystore.jks. Each Play app needs its own upload keystore.

Progress 19 September 2026 (chat screenshots): done. Internal testing Active, latest 3 (1.0.1), testers Google Testers. Production Inactive. Phone Play listing shows com.sourcectl.ariadne (unreviewed) with Uninstall / Open (test build, Business). Temporary package-name title is expected for Internal without a public listing — do not start Production to change it.

1. Create the keystore on GLaDOS ​

In Terminal, from /Users/tmin/Code/active/safecall:

bash
cd ariadne/android
keytool -genkey -v \
  -keystore upload-keystore.jks \
  -storetype JKS \
  -keyalg RSA \
  -keysize 2048 \
  -validity 10000 \
  -alias upload \
  -dname "CN=Ariadne Indoor Navigation, OU=Ariadne, O=SOURCE CONTROL SINGLE MEMBER P.C., C=GR"
cd ../..

keytool asks for a keystore password, then the same password again, then a key password. Use the same password for store and key. Put that password in a new Apple Passwords item named Ariadne Android upload keystore @ GLaDOS. Do not reuse the Keryx keystore password item.

Paste: Terminal line generated 2,048 bit RSA key pair / keystore saved as upload-keystore.jks. No password text.

2. Create key.properties ​

bash
cp ariadne/android/key.properties.example ariadne/android/key.properties

Open ariadne/android/key.properties and fill only the two password lines. Leave alias and storeFile as in the example:

properties
storePassword=<the password you just typed>
keyPassword=<the same password>
keyAlias=upload
storeFile=../upload-keystore.jks
bash
git check-ignore -v ariadne/android/key.properties ariadne/android/upload-keystore.jks

Both must print an ignore rule. Never git add them.

Paste: git check-ignore output. Not the file contents.

3. Copy both files to the NAS ​

  1. File Browser: http://192.168.178.213:8080.
  2. If secrets/ariadne/android/ is missing: create ariadne then android.
  3. Upload from GLaDOS:
    • /Users/tmin/Code/active/safecall/ariadne/android/upload-keystore.jks
    • /Users/tmin/Code/active/safecall/ariadne/android/key.properties

Paste: File Browser listing of secrets/ariadne/android/ showing those two filenames.

4. Create the Play app ​

  1. Open Play Console.
  2. Create app.
  3. App name: Ariadne Indoor Navigation.
  4. Default language: English.
  5. Type: App.
  6. Free or paid: Free.
  7. Check Developer Program Policies, US export laws, and Play App Signing Terms of Service.
  8. Create app.

Paste: Play Console app dashboard titled Ariadne Indoor Navigation.

5. Category ​

  1. Left menu Grow users → Store presence → Store settings.
  2. App category: Business.
  3. Save. Do not paste listing copy.

Paste: Store settings with category Business.

6. Czech Republic only ​

  1. Left menu Test and release → Testing → Internal testing.
  2. Open Countries/regions (on that page, or under Production if Internal is synced).
  3. Add Czech Republic. Remove every other country.

Paste: Countries list showing only Czech Republic.

7. Do not invent a privacy URL ​

If the dashboard blocks Create new release behind a privacy-policy URL: stop. Paste the dashboard card. Do not type https://safecall-lab.sourcectl.com as the Keryx/Play privacy URL.

8. Return to Apple if the IPA is not built yet ​

If releases/ariadne.aab does not exist, go back to OA-AT-ariadne-apple step 8 and run bun release:ariadneonce. Then continue.

9. Upload the AAB ​

  1. Play Console → Ariadne Indoor Navigation.
  2. Test and release → Testing → Internal testing.
  3. Create new release.
  4. Upload /Users/tmin/Code/active/safecall/releases/ariadne.aab.
  5. Confirm package com.sourcectl.ariadne.
  6. Release name can be First Internal AAB. Do not paste store listing copy.
  7. Save → Review and roll out / Roll out to Internal testing.

Do not open Production → Start rollout.

Paste: Internal testing release showing the uploaded version, status rolled out to testers.

10. Testers ​

  1. Same Internal testing page → Testers (or Testers and groups).
  2. Create an email list if none exists.
  3. Add the Google account signed in on the lab Android.
  4. Copy the opt-in URL from that page.

Paste: Testers tab with that Google account (email may be cropped to the domain).

11. Install on one Android ​

  1. On the phone, sign into that same Google account.
  2. Open the opt-in URL in Chrome.
  3. Accept Internal testing.
  4. Install Ariadne Indoor Navigation from Play. Not a sideload APK.
  5. Launcher label Ariadne. Do not pair yet.

Paste: Play listing or home screen showing Ariadne installed, Internal.

12. Production still off ​

Open Test and release → Production. It must not show an active rollout.

Paste: Production track Inactive / no rollout.

Verify

  • [x] Package com.sourcectl.ariadne (Internal AAB 3 / 1.0.1)
  • [x] upload-keystore.jks only on GLaDOS + NAS (gitignored)
  • [x] Internal AAB installed from Play (19 September 2026; listing title still the temporary unreviewed package name)
  • [x] Production not rolled out (19 September 2026)

Evidence: NAS evidence/ariadne-theseus/YYYY-MM-DD-oa-at-ariadne-play.txt (version code, “Internal only”). Tick log: OA-AT-ariadne-play → done (19 September 2026). GitLab: #162 closed 19 September 2026.

Forbidden: Production; copying the Keryx .jks; cz.wantok.ariadne.


OA-AT-maps — Phone walk (Internal Ariadne + lab URL) ​

Who: You, on a physical phone. Not a simulator. Issue: #148

Progress 19 September 2026: Internal Ariadne synced to lab, CEDR, 4. NP. Floor canvas stayed empty on iPhone and Android: the 4. NP PNG is 7015×4960, camera started at the white origin, min zoom 0.1 could not fit it. Camera-fit is in the Ariadne tree (#164). Needs a new Internal Ariadne 1.0.2 (4) (AAB+IPA in releases/, not bun release:ariadne). Then Transporter + Play Internal, then the walk. Do not close #148 until heading/position pass.

Gate (all required):

  • [x] https://safecall-lab.sourcectl.com/ loads (18 September 2026)
  • [x] Floor 4. NP has a map (import 18 September 2026)
  • [x] Ariadne installed from TestFlight or Play Internal (19 September 2026)

1. Open Mobile Access ​

  1. Browser: https://safecall-lab.sourcectl.com/login.
  2. Sign in as admin@sourcectl.com (Apple Passwords item SafeCall lab admin @ safecall-lab.sourcectl.com).
  3. Navbar Tools → Mobile Access (/tools/mobile-access).
  4. Use the Ariadne QR on that page. Not Keryx. Not Theseus.

Do not photograph the QR. Do not paste it.

Paste: Mobile Access page chrome with the Ariadne card visible. Crop or cover the QR pixels.

2. Pair Ariadne ​

  1. Open Internal Ariadne.
  2. Scan the live QR (camera on the monitor, not a photo).
  3. Connect → Sync.
  4. Location CEDR, floor 4. NP.

Paste: Navigation screen showing 4. NP and a floor plan. No QR.

3. Heading ​

Stand on 4. NP. Turn the phone. The player must be an arrow that rotates. Floor compass offset is 129°.

Simulator / no magnetometer: a pin that does not rotate is expected. Write that on the NAS note; it is not a software fail.

Paste: Map with the player glyph (arrow or pin).

4. Position ​

Bluetooth on. Stand next to imported beacon 4A.Chodba 4AD_1 / WTK_0E47EA if it is in range. Marker should sit in that quadrant, not across the floor.

No WTK_ heard: blocked on hardware. Write that; do not call it a code fail.

Paste: Map with the marker (no beacon MAC required in the photo).

Verify

  • [ ] 4. NP floor plan after sync
  • [ ] Heading pass, or documented no-compass pin
  • [ ] Position pass, or documented no radio
  • [ ] URL is https://safecall-lab.sourcectl.com

Evidence: NAS evidence/ariadne-theseus/YYYY-MM-DD-oa-at-maps.txt (pass/fail only). Tick log: OA-AT-maps → done. GitLab: Note on #148. Close #148 only after this walk.

Forbidden: QR photos; committing rasters; closing #148 from import.