Appearance
🛡️ adminadmin@example.com · Domain admin in Acme Corp
Chapter 11 — Access without keys
A bucket has nothing to log in to
So this chapter provisions something you can actually reach — a Node — and then reaches it without ever holding a credential for it.
bash
CATALOG_ID=$(plexctl blueprint catalog list --output json \
| jq -r '.items[] | select(.name == "Official Blueprint catalog") | .id')
plexctl blueprint import "$CATALOG_ID" kubernetes-cloudless-nodeSame catalog as chapter 8, second Blueprint imported from it.
Capture the node version
bash
NODE_BLUEPRINT_ID=$(plexctl blueprint list --all --output json \
| jq -r '.items[] | select(.slug == "kubernetes-cloudless-node") | .id')
NODE_VERSION_ID=$(plexctl blueprint get "$NODE_BLUEPRINT_ID" --output json \
| jq -r '.versions[] | select(.version == "v1alpha1") | .id')
echo "$NODE_VERSION_ID"A new Project, and the same governance again
bash
PROJECT_ID=$(plexctl project create \
--domain "$DOMAIN_ID" \
--slug reach-demo \
--display-name "Reach demo" \
--output json | jq -r '.id')
echo "project=$PROJECT_ID"bash
plexctl credential assignment request \
--project-id "$PROJECT_ID" \
--cloud-credential-id "$CREDENTIAL_ID"A second Project does not inherit the first one's credential. Every Project asks, and every request is decided.
🏗️ operatoroperator@example.com · platform grant, no Domain
Approve it, briskly
bash
APPROVAL_ID=$(plexctl approval list --profile operator --status pending-approval --output json \
| jq -r --arg cred "$CREDENTIAL_ID" \
'first(.items[] | select(.kind == "credential_assignment" and .target_resource == "cloudcredential:" + $cred) | .id)')
plexctl approval approve "$APPROVAL_ID" \
--profile operator --output json | jq '{state, materialised}'Exactly chapter 9's flow against a new Project. Do not re-narrate it — the point is that it is not optional the second time.
🛡️ adminadmin@example.com · Domain admin in Acme Corp
Provision the Node
bash
RESOURCE_ID=$(plexctl resource create \
--project-id "$PROJECT_ID" \
--kind node \
--blueprint-version-id "$NODE_VERSION_ID" \
--cloud-credential-id "$CREDENTIAL_ID" \
--parameters '{}' \
--output json | jq -r '.id')
echo "$RESOURCE_ID"--kind node, and an empty parameter object.
The phase the bucket skipped
bash
while true; do
PHASE=$(plexctl resource get "$RESOURCE_ID" --output json | jq -r '.provisioning.phase')
echo "$(date +%T) $PHASE"
case "$PHASE" in Ready|Failed) break ;; esac
sleep 10
donetext
15:02:02 Pending
15:02:12 Provisioning
15:02:42 Enrolling
15:04:12 ReadyEnrolling is new. The broker is waiting for the node to redeem a bootstrap token and register as a live mesh peer.
Ready here means the node came back and joined — not that a VM booted. Chapter 13 drives that same registration by hand.
Access is a grant, not a key
You can already reach this Node — a Domain admin's authority walks down Domain to Project to Resource.
A teammate who is not a Domain admin needs it written out. And you do not write act: that is a permission the schema computes. You write the smallest relation that adds up to it.
bash
MEMBER_ID=$(plexctl identity list --domain "$DOMAIN_ID" --output json \
| jq -r 'first(.items[] | select(.display_name == "member@example.com") | .id)')
plexctl rebac tuple add \
--project "$PROJECT_ID" \
--resource "resource:$RESOURCE_ID" \
--relation operator \
--subject "user:$MEMBER_ID"Ask for a kubeconfig
bash
plexctl kubeconfig \
--project-id "$PROJECT_ID" \
--resource-id "$RESOURCE_ID" \
--impersonate-user ops \
--output-file ./resource.kubeconfig \
--listener-timeout 0text
plexctl: kubeconfig: session issued but the target node has not reported a listener endpointRead that message carefully — it is the honest one.
The session was issued, signed, and audited. Then the wait ended empty-handed: the dev-stack simulator Node reports no listener, so there is nothing to point kubectl at.
What it writes when a Node does answer
yaml
apiVersion: v1
kind: Config
clusters:
- name: plexsphere
cluster:
server: https://<session-listener-endpoint>
users:
- name: plexsphere
user:
token: <one-time-session-jwt>No cluster credential anywhere in that file.
A one-time token, minted on this issuance, pointing at a listener the Node opened for this session.
The same shape for anything else
bash
plexctl tcp-forward \
--project-id "$PROJECT_ID" \
--resource-id "$RESOURCE_ID" \
--host localhost \
--port 5432 \
--listener-timeout 0bash
plexctl ssh \
--project-id "$PROJECT_ID" \
--resource-id "$RESOURCE_ID" \
--login-user ops \
--listener-timeout 0A generic mediated tunnel, and an SSH login that hands control to your local ssh client with the one-time token passed through the environment — never on the command line.
Same issuance path, same audit trail, three shapes.
Why this is the interesting slide
Every session carries a TTL — 30 minutes by default, a 4-hour ceiling, a 15-minute idle timeout. A sweeper reclaims them the moment they expire.
Every issuance is written to the per-Domain audit log under access.issue. Who reached what, when is a query, not a guess.
And a Domain can demand step-up authentication for a session kind, in which case issuance is refused until you re-authenticate.
Nobody is holding a key. There is no key to hold.