본문으로 건너뛰기
cccskills
무료GitHub에 공개

delete-user

Remove / reset a user (and their partition) on a running MeshWeaver portal (AKS). Use when you need to kick out a user — e.g. a throwaway test account — so they re-onboard from scratch. The normal delete API REFUSES a user's home ("user partition root … cannot be deleted"), so deletion is a Postgres-schema drop, not an MCP call. Covers where a user's data actually lives (per-user PG schemas named by BOTH id AND email, e.g. `rbuergi` + `rbuergi@systemorph.com`), how to reach PG (the connection string is inline in the portal's `ConnectionStrings__memex` env — NOT the `POSTGRES_PASSWORD` secret), the substring-match footgun (`buergi` also matches admin `rbuergi`), the in-memory resurrection gotcha (a recycle re-saves the node — a portal restart is what actually clears it), and the half-onboarded case where the user has NO schema at all (in-memory only → restart is the whole fix).

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Systemorph/MeshWeaver스타 12(GitHub 12 + cccskills 0)

실행 점수 미검증

설치 방법 보기

포함된 파일(1)

  • SKILL.md8.6 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

The cluster name, resource group and namespace list are deployment identities and live in the PRIVATE Systemorph/Memex repo — deployments/aks/envs.json (cluster + environments) and docs/deployments.md. Export them once per session rather than hard-coding them here:

eval "$(gh api repos/Systemorph/Memex/contents/deployments/aks/envs.json --jq '.content' | base64 -d \
  | jq -r '"AKS_RG=\(.cluster.resourceGroup) AKS_CLUSTER=\(.cluster.name) NAMESPACES=\"\([.environments[].ns]|join(" "))\""')"

Verified to set all three (resource group / cluster name / the namespaces) on 2026-08-19. Reading it from the source of truth also means a new environment shows up here automatically instead of this file going quietly stale.

/delete-user — remove a user + partition from a running MeshWeaver portal

Deleting a user is not an MCP/API operation — the framework guards it. This skill does it via the Postgres layer, safely.

1. Why the API refuses

The MCP delete (and DeleteNodeRequest) guards user partition roots:

Cannot delete '<user>': '<user>' is a user partition root (home) and cannot be deleted — that would remove the entire partition (threads, tokens, settings, access grants) and lock the user out.

So you cannot delete a user via delete @<user>. You drop their Postgres schema(s).

2. Where a user's data lives

public.mesh_nodes is empty by design; each partition is its own PG schema ({id}.mesh_nodes + satellite tables access, threads, activities, …). A user typically has TWO schemas — one by id and one by email:

user-a                      user-a@example.com
user-b                      …

So dropping <id> alone can leave <id>@<domain> behind. Drop both.

⚠️ Half-onboarded users have NO schema. If onboarding never provisioned the partition (the user created no content / a write failed), the User node lives only in the portal's in-memory workspace — nothing in PG at all. Then there is nothing to drop; step 5 (restart) is the whole fix. Verify with the find in step 4.

3. Reach Postgres

🚨 This whole procedure is break-glass by construction (maintainer, 2026-09-08: operations go through the control instance's Hosting API, not the cluster). There is no Hosting/InstanceAction kind for removing a user, so the schema drop below is a cluster write with no record and no audit — do it only on an explicit order, write it up as break-glass, and file the missing action against the Hosting package. The restart in step 5 IS an action kind: file a Restart Hosting/InstanceAction rather than the kubectl rollout restart shown. Policy: OperatingFromThePortal.md.

The private AKS cluster: kubectl only via az aks command invoke -g "$AKS_RG" -n "$AKS_CLUSTER" --command "…".

The DB password is inline in the portal's ConnectionStrings__memex env — NOT the POSTGRES_PASSWORD secret (that secret is a different, unused value; using it gives password authentication failed). Parse the real one out of the portal env and hand it to a postgres:16 client pod (the invoke shell has no sed/tr/python3 — use bash parameter expansion only). Pass SQL to the pod base64-encoded to dodge four levels of quoting.

Connection string shape: Host=<db-host>;Port=5432;Username=<db-admin>;Password=…;Database=<database>;SslMode=Require;…

4. FIND the user's footprint first (never drop blind)

Run this to see the exact schema names + confirm where (if anywhere) the user is persisted. Match on the full id, and beware: buergi matches BOTH roland.buergi and admin rbuergi — use the distinguishing part of the id (roland, not buergi).

# find_user.sh  — pass the user's DISTINCTIVE id fragment as $1 (e.g. "roland", not "buergi")
NEEDLE="${1:?usage: find_user.sh <distinctive-id-fragment>}"
cat > /tmp/find.sql <<SQL
\echo '== schemas matching =='
SELECT schema_name FROM information_schema.schemata WHERE schema_name ILIKE '%${NEEDLE}%';
DO \$\$ DECLARE r record; c int; BEGIN
  FOR r IN SELECT table_schema FROM information_schema.tables WHERE table_name='mesh_nodes' LOOP
    EXECUTE format('SELECT count(*) FROM %I.mesh_nodes WHERE mesh_nodes::text ILIKE %L', r.table_schema, '%${NEEDLE}%') INTO c;
    IF c>0 THEN RAISE NOTICE 'mesh_nodes HIT schema=% rows=%', r.table_schema, c; END IF;
  END LOOP;
END \$\$;
SQL
run_psql_pod /tmp/find.sql     # see the pod helper below

Zero schemas + zero mesh_nodes hits ⇒ half-onboarded (in-memory only) ⇒ skip to step 5.

5. DROP the schema(s), then RESTART the portal

Dropping the schema is not enough on its own: the portal's in-memory workspace resurrects the node on the next partition reactivation (a recycle @<user> disposes the hub but reactivation re-saves it — the version bumps and it's back). The reliable clear is a portal restart (nothing in PG to reload).

# 1) drop every schema whose name contains the DISTINCTIVE fragment (both id and email variants)
DROP SCHEMA "roland.buergi" CASCADE;
DROP SCHEMA "roland.buergi@gmail.com" CASCADE;   -- if it exists

# 2) clear the in-memory copy so it can't resurrect
az aks command invoke -g "$AKS_RG" -n "$AKS_CLUSTER" --command \
  "kubectl -n <namespace> rollout restart deployment/memex-portal-deployment; kubectl -n <namespace> rollout status deployment/memex-portal-deployment --timeout=300s"

After the restart, get @<user> should be Not found, and the user re-onboards on next login.

The psql-client-pod helper (run_psql_pod)

Put this in your script; it pulls the connection from the portal env and runs a base64'd SQL file in a throwaway postgres:16 pod. The password is only ever in the pod env (never printed).

run_psql_pod() {  # $1 = path to a .sql file
  local NS="${NS:?set NS to the portal namespace}" SQLFILE="$1"
  local CS host user pass db B64
  CS=$(kubectl -n $NS exec deploy/memex-portal-deployment -c memex-portal -- printenv ConnectionStrings__memex 2>/dev/null)
  host=${CS#*Host=};     host=${host%%;*}
  user=${CS#*Username=}; user=${user%%;*}
  pass=${CS#*Password=}; pass=${pass%%;*}
  db=${CS#*Database=};   db=${db%%;*}
  B64=$(base64 < "$SQLFILE" | tr -d '\n')
  kubectl -n $NS delete pod pgc --ignore-not-found >/dev/null 2>&1
  kubectl -n $NS run pgc --image=postgres:16 --restart=Never \
    --env="PGPASSWORD=$pass" \
    --env="CONN=sslmode=require host=$host port=5432 user=$user dbname=$db" \
    --env="B64=$B64" \
    --command -- bash -c 'echo "$B64" | base64 -d > /tmp/q.sql; psql "$CONN" -f /tmp/q.sql 2>&1' >/dev/null 2>&1
  for i in $(seq 1 40); do
    ph=$(kubectl -n $NS get pod pgc -o jsonpath='{.status.phase}' 2>/dev/null || true)
    { [ "$ph" = Succeeded ] || [ "$ph" = Failed ]; } && break; sleep 3
  done
  kubectl -n $NS logs pgc 2>&1
  kubectl -n $NS delete pod pgc --ignore-not-found >/dev/null 2>&1
}

Wrap the whole script in a file and invoke it with az aks command invoke … --command "bash script.sh" --file script.sh (the --file upload avoids inline-quoting hell; the invoke shell has kubectl but not sed/tr/python3).

Checklist

  1. Identify the user's distinctive id fragment (not a substring shared with admins).
  2. Find their schemas + mesh_nodes rows (step 4). Confirm you're touching only the right user.
  3. DROP SCHEMA "<id>" CASCADE; and DROP SCHEMA "<id>@<domain>" CASCADE; for each match.
  4. Restart the portal (step 5) — the in-memory workspace resurrects the node otherwise.
  5. Verify get @<user> → Not found; the user re-onboards on next login.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

async

무료

Forwarding AccessContext through async/reactive boundaries, and the no-async rule on the actor-model mesh. Use when writing or reviewing any hub-reachable / Blazor-view / agent-round / IIoPool code that creates, reads, or updates a mesh node, OR when a write "silently does nothing" / "Access denied" appears after a .Subscribe / IIoPool / Observable.Create hop. The async/await/ToTask/.Wait/.Result/.GetAwaiter/FirstAsync family and lost AccessContext are the two ways async boundaries cause wedges and storms. Grounded in AsynchronousCalls.md, SyncedMeshNodeQueries.md, AccessContextPropagation.md.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Systemorph/MeshWeaver122026년 10월 29일 업데이트

ci

무료

What CI proves, what it cannot prove, and how to write a gate that cannot pass on no evidence. Use when authoring or changing a GitHub Actions workflow, adding a required check, wiring a satellite repo CI, or reasoning about whether a green run actually covers your change. Covers the absolute no-skip-trapdoor rule (a gate must never carry continue-on-error on its input step nor an if that asks whether a secret is set — GitHub paints a skipped job the same colour as a passed one), the reusable workflow_call jobs satellite repos must call instead of hand-rolling, why runs on main are deliberately never cancelled, and the large body of in-mesh C# that no dotnet build or test ever type-checks.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Systemorph/MeshWeaver122026년 10월 29일 업데이트

debug

무료

Debug a deadlock / hang / timeout in the mesh — a test that times out, a hub handler whose response never arrives, a GetRemoteStream / GetMeshNodeStream / GetQuery that never emits, an agent round or per-instance-hub activation that parks forever. Use whenever something times out and you need to find WHERE the message flow breaks down. The timeout is the symptom, never the bug — raising it just hangs longer. Grounded in DebuggingMessageFlow.md.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Systemorph/MeshWeaver122026년 10월 29일 업데이트

deploy

무료

Deploy MeshWeaver — the two routes (shared AKS cluster, and Azure Container Apps via the Aspire test/prod modes) and the traps that make a deploy look done when it is not. Use when rolling a code change onto a portal, standing up or patching an environment, or verifying that what is running is what you built. FIRST RULE (maintainer, 2026-09-08): on AKS an operation is a Hosting/InstanceAction on the control instance (Roll, Restart, Suspend, Audit, Reconcile, …) executed by the in-cluster operator — az aks command invoke / kubectl are break-glass, never the procedure. Also covers the bootstrap build-image / set-image / restart sequence the operator runs for you, why an env deploy.sh is first-time setup and not a code-update path, why the database migration is a run-once Job that a helm upgrade runs — never a Deployment to roll — and the DbVersionGate that makes a portal refuse to serve ahead of its schema.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Systemorph/MeshWeaver122026년 10월 29일 업데이트

gui

무료

Build or review ANY MeshWeaver UI — layout areas, editable views, forms, tables, content editing, side-panel composers — by wiring the framework's EXISTING data-binding pieces, NEVER hand-rolling. The one rule is ALWAYS DATA-BIND, especially editable views: the backend layout area declares WHAT to render (a UiControl tree + node PATHS), and every value read / write-back happens on the GUI side through the per-node IMeshNodeStreamCache. Hand-woven binding — replicating a node into a /data/{id} copy + a save subscription, emitting HTML strings, hand-built selects/checkboxes, .Take(1) on a live stream, async/await/QueryAsync in a layout area — is the bug class behind "awaiting first data" / "renders empty" / a stuck editable view. Use when writing or reviewing a layout area, control, editor, form, table, or any data-bound view. Grounded in src/MeshWeaver.Layout/DataBinding/, Doc/GUI/DataBinding.md + Editor.md, the Edit extension (EditorExtensions.cs), and the node-bound editor controls.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Systemorph/MeshWeaver122026년 10월 29일 업데이트

i18n

무료

Localize every user-visible string as you write it — the portal ships English + German, so a hard-coded UI string is a bug that renders English for every viewer. Use whenever you write or review text a human reads on screen: buttons, labels, tooltips, aria-labels, placeholders, page titles, empty states, validation messages, toasts, dialog copy, menu entries, settings tabs, notifications, errors. Covers the two shapes (a Translation attribute on a declaration, a strings.en/de key everywhere else), the second key home in the plugins repo whose drift guard compares VALUES and merges core-first, the CurrentUICulture ban (it covers date and number FORMATTING too), and the list of things deliberately NOT translated.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

Systemorph/MeshWeaver122026년 10월 29일 업데이트

Systemorph의 스킬 모두 보기

이 스킬의 문제 신고