Solution
Reference solution for Hello Pico Hands (Kubernetes).
Reference solution
A working solution consists of the eight files shipped under downloads/, applied against a local minikube cluster:
01-namespace.yaml— thehandsNamespace that carries the entire Pico surface for this lab.02-hands-contract.yaml— the provider-neutral Hands contractConfigMap(identity, declared capabilities, rules, sample event).03-rbac.yaml— theServiceAccount, namespacedRole(get/patch only, restricted byresourceNamesto the two Pico-ownedConfigMaps), andRoleBindingthat together form the authorization boundary.04-state-configmap.yaml— the empty, Pico-owned stateConfigMapthe Hand mutates.05-events-configmap.yaml— the empty, Pico-owned eventsConfigMapthe Hand appends to.06-pico-engine.yaml— the single-shot Pico engine Pod (hello-hands-pico) that carries the Pico Element, mounts the Hands contract, evaluates the rules, invokes the KubernetesHands adapter, and writes evidence.event.json— the single event payload the Pico observes (a copy of the payload embedded in the Hands contract for out-of-cluster testing).verify.sh— the automatable verification script the walkthrough runs in Step 8. Static checks always run; the live path runs whenkubectland a cluster are reachable.
Commands (recap)
Run from work/hello-pico-hands-kubernetes/ after copying the downloads in place (see the walkthrough for the full setup):
kubectl apply -f 01-namespace.yaml
kubectl apply -f 02-hands-contract.yaml
kubectl apply -f 03-rbac.yaml
kubectl apply -f 04-state-configmap.yaml
kubectl apply -f 05-events-configmap.yaml
kubectl apply -f 06-pico-engine.yaml
kubectl -n hands wait --for=jsonpath='{.status.phase}=Succeeded' \
--timeout=120s pod/hello-hands-pico
bash verify.shExpected output
The Pico engine prints the full pico.hand.* lifecycle plus one normalized evidence line into its container logs:
pico[hello-hands-pico] pico.hand.requested capability=pico.state.set
pico[hello-hands-pico] pico.hand.authorized capability=pico.state.set
pico[hello-hands-pico] pico.hand.succeeded capability=pico.state.set
pico[hello-hands-pico] evidence: {"execution":{"actor":"hello-hands-pico","pico":"hello-hands-pico","capability":"pico.state.set","provider":"kubernetes","target":{"kind":"ConfigMap","namespace":"hands","resourceName":"pico-hello-hands-state","key":"greeted"},"requested_at":"...","status":"succeeded","reason":""}}
The Pico-owned state ConfigMap carries the greeting the Hand applied, and the events ConfigMap carries the normalized lifecycle:
Hello, Pico!
pico.hand.requested\npico.hand.authorized\npico.hand.succeeded
verify.sh re-runs the whole path and prints:
verify: OK — live — state=Hello, Pico!, lifecycle=[requested, authorized, succeeded]
Why this satisfies the objectives
- Provider-neutral Hands contract:
hands.jsonandrules.jsonin02-hands-contract.yamlname only capabilities and constraints — nokubectl, no cluster verbs, no provider SDKs. A differentHandsProvider(e.g.LocalHands) could consume the same contract and implementpico.state.setagainst its own target without any change to the Pico or its rules. - KubernetesHands adapter: the Pico engine Pod’s shell command in
06-pico-engine.yamlis the whole adapter — it maps the contract’spico.state.setcapability to exactly onekubectl patchcall against the target named in the contract. - Identity before action: every
kubectlcall the adapter makes travels as thepico-hello-handsServiceAccount(the Pico’s on-cluster identity), and the emittedevidence.jsonrecords the Pico’s own identity fromidentity.jsonas theactor. - Authorization boundary: the namespaced
Rolein03-rbac.yamlgrants onlygetandpatchon ConfigMaps and is further constrained byresourceNamestopico-hello-hands-stateandpico-hello-hands-events. Any other verb, any other resource, or any other name is rejected by the API server — not by the adapter script. This is what makes the boundary trustworthy. - Allowlisted reversible action confined to a Pico-owned ConfigMap:
pico.state.setis the only allow rule inrules.json; the mutation is a merge-patch that sets one key on aConfigMapthe Pico exclusively owns, and Step 9 shows how re-applying04-state-configmap.yamlreverses it. - Normalized
pico.hand.*events + evidence: the append-only eventsConfigMapcarriespico.hand.requested,pico.hand.authorized, andpico.hand.succeeded(or thedenied/failedvariants), and the Pod’s own logs carry the single normalized evidence JSON. Both are captured underbuild/hello-pico-hands-kubernetes/as the lab’s runtime evidence. - Composio and non-Kubernetes runtimes are out of scope: the static path of
verify.shfails the whole run if any ofCOMPOSIO,composio,gmail,slack.com,githubtoken, orOPENAI_API_KEYappear in the engine script.