Files
claudetools/session-logs/2026-06/2026-06-22-mike-packetdial-buildout-oitvoip-vault.md
Mike Swanson f16a1a7084 sync: auto-sync from GURU-5070 at 2026-06-22 08:23:12
Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-22 08:23:12
2026-06-22 08:24:06 -07:00

9.8 KiB

Session — OITVOIP key vaulted + packetdial skill fully built out; SC admin handoff

User

  • User: Mike Swanson (mike)
  • Machine: GURU-5070
  • Role: admin

Session Summary

Picked up Howard's parked Tier-1 item #2 (packetdial non-functional, no creds). Mike pointed at C:\Users\guru\Downloads\OIT-API.txt, which held the OITVOIP/PacketDial NetSapiens reseller API key. Vaulted it at msp-tools/oitvoip.sops.yamlcredentials.api_key (the exact path/field the packetdial skill reads), documented it in the entry, and live-verified with ns.py whoami: key-id nsr_hSGUB5Wo, scope Reseller (91912.service), user/domain: *, read-write. Works against the skill's default host pbx.packetdial.com/ns-api/v2 (no api.ucaasnetwork.com override needed). Published the vault and told Howard his item was unblocked.

RTFM'd the live NetSapiens API to ground the skill: fetched the OpenAPI spec (v2 v44.4.10, 239 paths / 354 operations), mapped the surface (nearly everything nested under /domains/{domain}/...), and live-probed real response shapes. The pre-existing packetdial skill predated the key, so it had unverified guessy paths; I added 6 live-verified read wrappers (callqueues, timeframes, sites, contacts, autoattendants, billing) and rewrote SKILL.md with a capability map + the live credential status, replacing the stale "not yet provisioned" section.

On Mike's go (ACG domain arizonacomputerguru is not in production use — sanctioned test bed), added gated write wrappers for call-queue and time-frame edits and verified full create→update→delete lifecycles live. Then finished the wrapping across the rest of the platform: DID update/delete, per-user device CRUD, E911 address CRUD, contact CRUD, site create/update, auto-attendant create, SMS-number CRUD, blocked-number filters, MoH TTS create/delete, plus reads for addresses/smsnumbers/blocked-numbers/moh/dialrules/recording/transcriptions. Verified what was safely reversible on the test domain ([V]); marked the rest [P] (plumbed per spec, not lifecycle-verified — test domain lacks the feature or needs a special body) with the gotchas documented. Every write stays --confirm-gated; anything unwrapped remains reachable via raw.

Closed out coordination: confirmed all of Howard's coord messages from the prior day were handled (BUG-018/019/021 merged+deployed, Event Log Watch UI promoted to prod). The one open item was the ScreenConnect RESTful API Manager GetSessions ask; Mike granted Howard SC admin, so I handed it back to Howard via coord with the finding that the "Edit Extension Settings" dialog is config-only and the list-all methods live in the extension's code package (re-upload a modified build, or use the native session report API), not a setting.

Key Decisions

  • Vault path/field: stored the OITVOIP key at msp-tools/oitvoip.sops.yaml credentials.api_key specifically because that's what ns_client.load_credentials() reads (apikey mode); no skill code change needed.
  • Host: kept the skill on pbx.packetdial.com (key works there). Documented that the key's docs reference api.ucaasnetwork.com (same NetSapiens platform, white-label) with a PACKETDIAL_API_BASE_URL override path if a future endpoint 403s on pbx..
  • Wrapper philosophy: read-first; every write gated behind --confirm. Wrapped the practical MSP resource set; left multipart MoH upload, deep dialrule writes, and queue-agent login/logout PATCH on raw (awkward shapes, low value).
  • Timeframe writes are body-discriminated — same path, the server picks the op from the body — so one passthrough wrapper per verb (create/update/delete) covers all the spec's PUT/POST/DELETE variants correctly.
  • Honest verification status in docs: marked each wrapper [V] (lifecycle-verified on ACG) or [P] (plumbed per spec, unverified) rather than implying everything was tested, since several resources can't be exercised on the empty test domain.
  • ScreenConnect: handed the GetSessions work to Howard (now SC admin) rather than chasing it — it's his skill, not a blocker, and exposing the method needs an extension-package change, not a UI setting.

Problems Encountered

  • /tmp read failure (documented Windows gotcha): curl -o /tmp/x.json then Windows-python read failed (Git-Bash vs tool path mismatch). Switched to a repo-relative path (.claude/tmp/). This is the exact rule promoted to CLAUDE.md CORE the prior session.
  • OpenAPI JSON read raised cp1252 UnicodeDecodeError on Windows python — fixed by opening with encoding='utf-8'.
  • raw syntax: raw <path> failed (argparse expects raw <METHOD> <path>); corrected probes to raw GET <path>.
  • cargo-style env trap n/a here, but a related one: the contact-delete test couldn't parse the new id — the contacts read keys the id as unique-id, not contact_id. Deleted the leftover test contact by its unique-id; the wrapper itself is correct (the {contact_id} path param takes the unique-id value).
  • smsnumbers read = HTTP 400 on ACG ("include domain and dest or number") — SMS isn't provisioned on that domain, not a wrapper bug; marked [P].
  • number-filters quirk: block-numbers returned 202 but the number didn't persist (empty list after), and unblock-numbers (DELETE+body) returned an answering-rule guard error on the empty domain. Wrappers match the spec; behavior inconclusive on the empty test domain → marked [P], no leftover (verified list empty).
  • agent ghost rows: the gururmm host has multiple "gururmm" RMM agent records (re-enroll churn); resolving [0] grabbed a stale June-11 ghost (commands stuck pending). Selected the live row by recent last_seen. (Carried over from the prior session's build-verification work.)

Configuration Changes

Created:

  • msp-tools/oitvoip.sops.yaml (VAULT repo) — OITVOIP reseller API key + docs.
  • session-logs/2026-06/2026-06-22-mike-packetdial-buildout-oitvoip-vault.md (this file).

Modified (claudetools, .claude/skills/packetdial/):

  • scripts/ns_client.py — added read methods (callqueues, timeframes, sites, contacts, autoattendants, billing, addresses, smsnumbers, blocked_numbers, moh, dialrules, recording, transcriptions) + write methods (callqueue CRUD + agents, timeframe CRUD, DID update/delete, device CRUD, address CRUD, contact CRUD, site create/update, autoattendant create, smsnumber CRUD, block/unblock numbers, moh TTS create/delete).
  • scripts/ns.py — subcommands + dispatch for all of the above (writes --confirm-gated).
  • SKILL.md — live credential status, expanded command lists, capability map (RTFM), additional resource-edits section with [V]/[P] legend.
  • references/api.md — history updated (key provisioned, RTFM, wrappers added).

Credentials & Secrets

  • OITVOIP / PacketDial NetSapiens reseller API key — vaulted at msp-tools/oitvoip.sops.yamlcredentials.api_key. key-id nsr_hSGUB5Wo, scope Reseller 91912.service, read-write. Retrieve with bash .claude/scripts/vault.sh get-field msp-tools/oitvoip.sops.yaml credentials.api_key. (Full nsr_ secret intentionally NOT written here — it's in the vault. Plaintext source C:\Users\guru\Downloads\OIT-API.txt still on disk; Mike to delete.)
  • No other new secrets.

Infrastructure & Servers

  • NetSapiens API: https://pbx.packetdial.com/ns-api/v2 (v44.4.10). Token endpoint …/v2/tokens. OpenAPI …/webroot/openapi/openapi.json. White-label alt host api.ucaasnetwork.com (same platform). voip.packetdial.com = portal only (no API).
  • Reseller territory = 3 domains: arizonacomputerguru (ACG, test bed — not in production use), 0000.91912.service, russo.91912.service.
  • ScreenConnect: computerguru.screenconnect.com; RESTful API Manager extension, API user acg-sc-api; Howard now has SC admin.

Commands & Outputs

  • Vault store: bash .claude/skills/vault/scripts/vault-helper.sh new msp-tools/oitvoip.sops.yaml --kind api-key --set api_key=… --set api_key_id=nsr_hSGUB5Wo …
  • Verify key: ns.py whoami{"user-scope":"Reseller","reseller":"91912.service","readonly":"no",…}
  • Read wrappers (verified): ns.py callqueues|timeframes|sites|contacts|billing|addresses|moh|blocked-numbers <domain>
  • Verified write lifecycles on arizonacomputerguru: callqueue 8999 create→update→delete; timeframe always create→convert→delete; contact create→delete (id=unique-id). All returned 202/clean; no leftovers.
  • Spec map: 239 paths / 354 ops (GET 139 · POST 87 · PUT 68 · DELETE 50 · PATCH 10).

Pending / Incomplete Tasks

  • ScreenConnect GetSessions/list-all — Howard's, he now has SC admin (coord 58ed0b21). Exposing it needs an extension-package change or the native session report API; not a blocker.
  • [P] packetdial wrappers not lifecycle-verified: DID update/delete, device CRUD, E911 address CRUD (needs addresses/validate → pidflo), site/AA writes, smsnumber CRUD (SMS not provisioned on ACG), block/unblock numbers (202 didn't persist on empty domain), moh create/delete. Verify on a real domain when a task needs them.
  • OIT-API.txt plaintext in Downloads — Mike to delete.
  • Optional: AgentDetail-embedded Event Log Watch panel (Howard noted as a follow-up after the dirty AgentDetail branch lands).

Reference Information

  • Vault entry: msp-tools/oitvoip.sops.yaml (key-id nsr_hSGUB5Wo).
  • claudetools commits this session: f8…/earlier vault push e705b66 (vault repo); packetdial build-out d75c367b (initial), f42f9c24 (callqueue+timeframe edits), 1062a5ed (finish wrapping).
  • coord sent: 7726d9f4 (packetdial unblocked), 58ed0b21 (Howard SC admin handoff).
  • Skill: .claude/skills/packetdial/ (SKILL.md, references/api.md, scripts/ns.py, ns_client.py).
  • Local spec copy: .claude/tmp/ns-openapi.json (gitignored; refetch from the OpenAPI URL above).