| name | drphub-anon-access |
| description | Query DRP Hub public cards without auth. |
| version | 1.0.0 |
| author | Hermes Agent |
| license | MIT |
| platforms | ["linux"] |
| metadata | {"drphub":{"tags":["drp","hub","cards","rest-api","supabase","anon-key","punch4nfdi"],"homepage":"https://drphub-p4n.aip.de","api_base":"https://drp-term.kube.aip.de/api/v1","related_skills":["drphub-cards","drp-hub"]}} |
DRP Hub: Anonymous Access & Client-Side Maturity Computation
Problem
The DRP Hub REST API (drp-term.kube.aip.de/api/v1) requires authentication even for public products — GET /products?visibility=public returns 401 without a Bearer token. However, the web frontend embeds a Supabase anonymous key that enables read-only queries against the drp_cards table.
Additionally, maturity_level in the raw database is always 0. The actual maturity is computed server-side via computed_maturity_level (REST API only) or client-side by the frontend JS using gate-checking logic.
Solution: Extract Anon Key from Frontend JS
Step 1: Find the current JS bundle
The frontend bundles change filename on each deploy. Find the current one:
curl -s https://drphub-p4n.aip.de/ | grep -oP 'href="/([^"]*index[^"]*\.js)"' | head -1
Step 2: Extract the anon key
curl -s "https://drphub-p4n.aip.de/assets/index-*.js" | grep -oP '_o="\K[^"]+'
Step 3: Query drp_cards via Supabase REST
import re, json, urllib.request, subprocess
from urllib.parse import urlencode
js = subprocess.check_output(["curl", "-s", "https://drphub-p4n.aip.de/assets/index-*.js"]).decode()
anon_key = re.search(r'_o="([^"]+)"', js).group(1)
SUPABASE_URL = "https://rrgnjinkabvqavwwzyfs.supabase.co"
headers = {"apikey": anon_key, "Authorization": f"Bearer {anon_key}"}
params = {"select": "*", "visibility": "eq.public", "deleted_at": "is.null", "order": "last_modified.desc"}
url = f"{SUPABASE_URL}/rest/v1/drp_cards?{urlencode(params)}"
req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req) as resp:
cards = json.loads(resp.read().decode())
Client-Side Maturity Computation
Gate Requirements (reverse-engineered from frontend JS)
The frontend's be() function in DRPCard-B26H0C_j.js defines these gates. The fe() function computes: auto-level = highest level where all gates pass, then applies maturity_override if set.
def t(s):
return bool(s) and len(str(s)) > 0
def S(s):
return bool(s) and len(str(s)) > 0
def has_reana(card):
if card.get("has_reana"):
return True
yaml = card.get("reana_yaml") or card.get("reanaYaml")
return bool(yaml)
def compute_maturity(card):
r = has_reana(card)
l0 = [t(card.get("title")), t(card.get("description"))]
l1 = [t(card.get("git_url")), t(card.get("entry_command")) or r]
l2 = [t(card.get("license")), t(card.get("citation_cff_url")), t(card.get("release_tag"))]
l3 = [t(card.get("workflow_file")) or r, t(card.get("last_run_id")),
bool(card.get()) (card.get(, {})) > ]
l4 = [S(card.get()), t(card.get()),
(card.get()), card.get() == ,
(card.get())]
levels = [l0, l1, l2, l3, l4]
m =
i, reqs (levels):
(reqs):
m = i
:
override = card.get()
(override, (, )) <= override <= :
level = (override)
:
level = m
level, m
Pitfalls & Gotchas
1. Frontend L4 labels are misleading
The web UI shows "L4" on cards whose title/description text mentions "L4" — NOT because the card actually achieved L4. Always compute gates or query /products/{id}/maturity to verify.
Example: "Maturity walkthrough" displays as L4 on the web UI but its API maturity_level is 0, computed_maturity_level is 1. It fails L2 (missing env_image) and L4 (human_reviewed=false).
2. Supabase anon key limitations
- Only accesses the legacy
drp_cards table, not drp_products
- Cannot access
computed_maturity_level, maturity_gates, or maturity_missing
- JS bundle filename changes on each deploy
3. drp_cards vs drp_products schema
drp_cards (legacy): no maturity_level column, no computed_maturity_level
drp_products (new): has maturity_level (always 0 in practice); computed_maturity_level is a REST API-only computed field
- Both tables share the same
id UUID