/revivifyHow Revivify works
Revivify restarts a Kubernetes Deployment and follows the rollout. You cast it from Slack or a terminal as /revivify followed by a Deployment name; a collector inside your cluster resolves the name to a Deployment in scope and makes one strategic-merge patch, stamping kubectl.kubernetes.io/restartedAt on the pod template. That is the same thing kubectl rollout restart does, so Kubernetes runs the rollout under the workload's own rollingUpdate strategy, readiness probes, and grace period. The collector then polls the Deployment every 2 seconds until it is done or a 300-second deadline passes, and reports which happened. It never rolls back. The grant is get, list, and patch on Deployments plus read on pods; it holds no delete verb and no exec, so a restart cannot degrade into deleting or scaling pods. Casting it needs an entitlement, restart.revivify.cast, that a broad read grant does not include, and by default it refuses any target that is not dev or staging.
Caster is pre-launch. This page describes how Revivify is built, not a product you can install today. The transcript figures come from the demo on this site, and they are labelled where they appear.
How it works
One patch, and Kubernetes runs the restart
The action is a single strategic-merge patch to the Deployment, setting kubectl.kubernetes.io/restartedAt on spec.template.metadata.annotations to the time the cast started. Changing the pod template is what makes it a rollout: the Deployment controller rolls a new ReplicaSet and replaces pods under the strategy already configured on the workload, honouring its maxSurge, maxUnavailable, readiness probes, and terminationGracePeriodSeconds. This is the same mechanism kubectl rollout restart uses. Revivify does not invent a restart; it asks Kubernetes for the one you already configured.
The restart id is derived from the cast rather than generated fresh, so a re-delivered request is the same rollout, not a second one. No pod is deleted, and none could be: the grant behind Revivify is patch, get, and list on Deployments with read on pods, and there is no delete verb anywhere in it. The crude version of a restart, killing pods and hoping the controller notices, is unavailable to a cast and to a bug behind one.
It watches on its own clock, and does not improvise
After the patch, the collector polls the Deployment with get every 2 seconds. There is no watch verb in the grant, so the rollout is followed by polling, not by a streaming watch. It ends at one of four outcomes: done when every replica is updated and ready, timed out if the spell's own 300-second deadline passes first, a status error if the Deployment reports one, or cancelled. The deadline is the spell's, not the Deployment's progressDeadlineSeconds, so a workload with a generous deadline does not hold the cast open for as long as it likes.
It does not roll back for you. A rollback changes what is running, which is a different action with a different blast radius, and it is a different spell. A timed-out or errored rollout is reported as degraded, at the replica count it reached, rather than dressed up as a success.
Production is refused by default, and the write is a separate grant
Revivify takes a target, and the default set is dev and staging. A cast against anything else is refused at preflight and refused again inside the invoke path, so an unpinned target, or one that is not on the allowlist, does not slip through by racing the check. Production is refused under any name you give it. The policy is fail-closed: when the target is unclear, the safe answer is the one you get.
The gate is not the read grant. Casting Revivify needs an entitlement, restart.revivify.cast, that lives outside the spell.* family on purpose, so a person or agent can hold every read spell in the book and still restart nothing. There is no per-workload ownership behind this: the entitlement plus the collector's namespace scope decide what a cast can reach, which means restart.revivify.cast lets you restart any Deployment the collector can see in the namespaces it is scoped to. The bound is the namespace and the target policy, not a map of who owns what.
It runs in your cluster; the status comes back through the control plane
A collector runs inside your cluster and dials the control plane outbound over mTLS. The patch is made in place, under a Kubernetes ServiceAccount your team scopes; nothing dials in, and no kubeconfig, cloud credential, or shell leaves the cluster. The control plane cannot widen that ServiceAccount, because the namespaces and the RBAC that bound it are set on your side.
What travels back is rollout status, stored in the cast's row in the control plane. It is status, not your logs, unless you also ask Revivify for a final tail; that optional log read is what the LogSource trait adds, and it is why the grant carries read on pods as well as patch on Deployments.
Who is asking
Identity comes from your own IdP over OIDC. In the terminal a device-flow sign-in caches a short-lived access token and a refresh token at file mode 0600, refreshed silently as they expire; from Slack, Socket Mode maps your Slack user to a subject and the roles it holds. The collector holds the ServiceAccount that makes the patch. You hold an entitlement, not a credential.
An agent enrolls as its own principal. Grant it spell.scry.cast and withhold restart.revivify.cast, and it can read a service and cannot restart it. That refusal is not a check the code chooses to run at the right moment; it is a string the agent was never given.
Setting it up
Setup happens once, run by whoever already administers the cluster. Everyone who casts a spell afterward holds none of what setup touches. What follows is the model laid out in enough detail to check; nothing here runs today.
Run the collector in your cluster
The collector is deployed into its own namespace with its own ServiceAccount. It needs no inbound networking, because it opens the connection to the control plane itself, outbound over mTLS. SPELLBOOK_NAMESPACES pins the namespaces it may ever touch, set here, on your side, where the control plane cannot change it.
collector envSPELLBOOK_NAMESPACES=storefront,checkoutBind the restart grant
Revivify's ServiceAccount gets get, list, and patch on Deployments and read on pods. This is the ceiling the API server enforces. There is no delete and no scale in it, so a restart cannot become a deletion or a resize even through a bug on our side.
revivify ClusterRolerules: - apiGroups: ["apps"] resources: [deployments] verbs: [get, list, patch] - apiGroups: [""] resources: [pods, pods/log] verbs: [get, list]Grant the entitlement, pin the targets
Two strings, deliberately apart. spell.revivify.cast puts the spell in reach; restart.revivify.cast is the write entitlement, held outside the spell.* family so a read role cannot carry it by accident. The target allowlist defaults to dev and staging, and production is not on it.
entitlementsspell.revivify.cast restart.revivify.cast # targets: dev, staging (prod refused under any name)Cast it
Ana signs in as herself and restarts checkout in staging. She holds no kubeconfig, and the cast is a casts row before the patch is sent.
slack/revivify checkout-api --target staging
This is the design, not an install guide. The collector bundle, the flags, and the manifests are not something you can apply today.
What it never receives
The list a security review asks for, on the page rather than in a PDF behind a form.
- No kubeconfig and no cluster credential. The collector's ServiceAccount stays in the cluster, and you are never issued one.
- No shell. Revivify's grant has no exec, attach, or port-forward, so there is no path from a cast to a prompt inside a pod.
- No delete verb, anywhere. It cannot delete a pod, a ReplicaSet, or a Deployment, so a restart cannot turn into a deletion.
- No scale, no image edit, no env edit. Revivify restarts a workload; it does not change what that workload is.
- No cloud credential. Revivify patches a Deployment; it holds no IAM role or registry access.
What it refuses to do
A permission that was never issued cannot be misused, by a person, by a compromised account, or by an agent.
- /revivify checkout-api --target prod
- Refused. The target allowlist is dev and staging by default, and production is off it under any name. The refusal fires at preflight and again inside the invoke path, so it cannot be won by racing the check. There is no override flag, because an override flag is a permission you would be holding.
- kubectl rollout restart deploy/checkout-api
- There is nothing to revoke here, because kubectl was never issued to you. The spell is the only path, so the version of this that can be aimed at the wrong namespace or the wrong environment does not exist for you.
- Restart with only read spells granted
- Refused. Restarting needs restart.revivify.cast, which lives outside the spell.* family. Holding every read spell in the book does not add up to a restart, because the write was never granted.
- Restart a StatefulSet or a DaemonSet
- Revivify resolves a Deployment by exact name and patches a Deployment. A StatefulSet or DaemonSet is neither, and neither is in the grant. Restarting a stateful workload is a different blast radius, and it will be a different spell before it is a flag on this one.
- Scale it, change its image, edit its env
- Not this spell and not this grant. Revivify's only write is the restart annotation on the pod template. There is no apply-this-manifest and no run-this-command behind it.
- Roll it back for me
- Revivify restarts; it does not roll back. A rollback changes what is running, a different action with a different blast radius. If a rollout times out, Revivify says so and stops, rather than reverting on your behalf.
The audit record
A restart is a write, and every cast is a casts row before the patch is sent. The durable record is that row, so there is no separate audit table to fall out of sync with it. A cast refused at preflight, a prod target or a missing entitlement, never becomes a row: it is logged as a warning, so a queryable casts row always describes a restart that actually ran.
cast_id cst_9f31c0b4
tenant_id tnt_northwind
principal okta|00u1a4h2 (oidc subject, not an email)
castable revivify
idempotency_key slack:U03QK4T2P:1753281187
state succeeded
degraded false
started_at 2026-07-23T14:33:07Z
finished_at 2026-07-23T14:33:48Z
result { surface: slack #checkout-oncall,
target: checkout-api / storefront, env: staging,
patch: kubectl.kubernetes.io/restartedAt,
rollout: 18 -> 19, replicas_ready: 3/3 }Where a record lands
- The casts row in the control plane's database, isolated per tenant with row-level security. This is the durable record, and it is the one a query runs against.
- The tenant's Slack audit_channel, which mirrors the result of every terminal cast, so a channel of record exists even for casts nobody ran in Slack.
- The collector's stdout as structured JSON, so whatever log pipeline you already run picks it up with no new integration.
- Your API server's own audit log records the collector's patch independently, and the cast id ties the two accounts together.
Failure modes
What happens when it cannot do the thing.
- The new pods never become Ready
- The poll runs to the spell's 300-second deadline, then reports timed out at the replica count it reached, marked degraded. Revivify does not roll back, so under a default rolling strategy the workload sits at reduced capacity until you act, and the report says that plainly rather than reporting success.
- The Deployment reports a status error mid-rollout
- The cast ends in a status error, degraded, carrying what the Deployment said.
- The name is not a Deployment, or matches nothing
- It refuses. Revivify resolves a Deployment by exact name in scope, so a StatefulSet, a typo, or an out-of-scope namespace does not resolve, and nothing is patched.
- The entitlement or the RBAC is missing
- The cast fails before the patch and names what is missing, the restart.revivify.cast string or the patch verb, so whoever administers it fixes one line instead of bisecting an RBAC error.
- The restart runs but the symptom stays
- Revivify reports that the pods came back and stops there; it cannot tell whether the underlying cause is gone. That read is /scry, so check before you cast it again.
Questions
How do I restart a Kubernetes Deployment without kubectl access?
Cast /revivify followed by the Deployment name, from Slack or a terminal. A collector inside your cluster resolves the name to a Deployment in scope and makes one strategic-merge patch, stamping kubectl.kubernetes.io/restartedAt on the pod template. Kubernetes then runs its normal rolling restart under the workload's own strategy and readiness probes. You never hold a kubeconfig or a shell, and the collector's grant is get, list, and patch on Deployments with no delete verb at all.
What does Revivify actually do to my cluster?
One thing: a strategic-merge patch that sets kubectl.kubernetes.io/restartedAt on spec.template.metadata.annotations. That is the same annotation kubectl rollout restart writes. Changing the pod template makes the Deployment controller roll a new ReplicaSet and replace pods under the strategy already on the workload. Revivify does not delete pods, does not scale, does not edit the image or the environment, and does not roll back.
Can Revivify delete or scale a workload?
No. Its grant is get, list, and patch on Deployments plus read on pods, with no delete and no scale verb. The crude form of a restart, deleting pods and letting the controller replace them, is not something Revivify can do, because the API server rejects a verb the ServiceAccount does not hold. A restart here is always the rolling one Kubernetes runs from the annotation.
Can Revivify restart a production service?
Not by default. Revivify takes a target, and the allowlist is dev and staging; production is refused under any name, at preflight and again inside the invoke path, so the refusal cannot be raced. Changing that allowlist is a deliberate act by whoever administers the control plane, not a flag a caster passes. There is no per-workload ownership either: the entitlement and the collector's namespace scope decide what a cast can reach.
Can I give an AI agent permission to restart a service?
Yes, and without a credential. An agent enrolls as its own principal. Grant it spell.revivify.cast and restart.revivify.cast on the targets it operates and it can restart those Deployments; withhold restart.revivify.cast and it can hold every read spell and still restart nothing. The write is a string it was never given, not a check it might slip past.
Does Revivify roll back a bad rollout?
No. Revivify watches the rollout until every replica is ready or its 300-second deadline passes, and reports which happened. A timed-out or errored rollout is reported as degraded, at the replica count it reached; Revivify does not revert. A rollback changes what is running, which is a different action, and it is a different spell.
The machine-readable version of this page is at /spells/revivify.md. It is generated from the same source, so it says the same thing.
Run this in your own cluster.
Caster is not open yet. Leave an address and we will come to you first.