> ## Documentation Index
> Fetch the complete documentation index at: https://ryvn.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Set up just-in-time kubectl access in BYOC

> Get customer-approved, time-boxed kubectl access to troubleshoot your application in a customer's cloud.

Set up customer-approved `kubectl` access to troubleshoot your application in a customer's cloud. This guide limits access to pod inspection and exec in `app-system`, with a 30-minute grant.

Just-in-time access means you hold no standing credentials for the environment. You request access when you need it, your customer approves it, and the access ends on its own when the window closes.

The example uses a `support-debug` permission set, a `customer-prod` environment, the `app-system` namespace, and an `api` installation. Substitute your own names.

## Before you start

| Actor                     | Does                                             | Needs                                        |
| ------------------------- | ------------------------------------------------ | -------------------------------------------- |
| Environment administrator | Writes the permission set, sets the Connect mode | `admin` on the org                           |
| Your customer             | Approves or denies requests, revokes grants      | `developer` or `operator` on the environment |
| You                       | Request access, run `kubectl`, revoke when done  | `developer` or `operator` on the environment |

The requester and the approver must be different identities. The server rejects an approval by the identity that made the request.

You also need:

* `customer-prod` running components that support permission sets. See [provisioning prerequisites](/docs/provision/how-provisioning-works).
* kubectl v1.31 or newer. Ryvn bridges pod exec over WebSocket, and older kubectl negotiates SPDY instead.
* `app-system` managed by Ryvn in that environment, either declared on the environment or created for an installation. A `namespaceNames` scope does not cover namespaces Ryvn does not manage.
* A namespace that is not reserved. Ryvn refuses `ryvn-system`, `kube-system`, `kube-public`, `kube-node-lease`, and `observability`.

Read the installation's namespace instead of guessing it:

```bash theme={null}
ryvn describe installation api -e customer-prod
```

```
Name:         api
Environment:  customer-prod
Service:      api (kubernetes)
Namespace:    app-system
```

## Step 1: Create the permission set

Environment administrator. A permission set is a named set of Kubernetes RBAC rules paired with a Ryvn scope.

<Tabs>
  <Tab title="CLI">
    ```yaml theme={null}
    # permission-set.yaml
    # yaml-language-server: $schema=https://api.ryvn.app/v1/schemas/resources.json
    kind: PermissionSet
    metadata:
      name: support-debug
    spec:
      description: Temporary support access to the application namespace
      kubernetes:
        permissions:
          - scope:
              namespaceNames:
                - app-system
            rules:
              - apiGroups: [""]
                resources: [pods]
                verbs: [get, list, watch]
              - apiGroups: [""]
                resources: [pods/exec]
                verbs: [create]
    ```

    ```bash theme={null}
    ryvn create -f permission-set.yaml
    ```
  </Tab>

  <Tab title="Dashboard">
    Go to **Settings** → **Permission Sets** → **Create Permission Set**. Enter the name and description in their own fields, then paste the definition into the YAML editor. The editor takes the `kubernetes:` block at the top level, without `kind`, `metadata`, or `spec`:

    ```yaml theme={null}
    kubernetes:
      permissions:
        - scope:
            namespaceNames:
              - app-system
          rules:
            - apiGroups: [""]
              resources: [pods]
              verbs: [get, list, watch]
            - apiGroups: [""]
              resources: [pods/exec]
              verbs: [create]
    ```
  </Tab>
</Tabs>

Pod permissions let you find a pod. The exec rule lets you run a command inside it — Kubernetes authorizes exec as `create` on `pods/exec`, so that verb is the one the set needs.

Add pod logs only if your team needs them:

```yaml theme={null}
- apiGroups: [""]
  resources: [pods/log]
  verbs: [get]
```

Do not add `secrets`, wildcards (`*`), or a `cluster` scope. Every rule you add applies for the full duration of every grant issued against the set.

Read back what you published:

```bash theme={null}
ryvn describe permission-set support-debug
ryvn get permission-set support-debug -o yaml
```

Requests and grants keep the revision approved for them, even if you later edit the permission set. Editing publishes a new revision when the rules change; renaming the description or saving unchanged rules does not.

## Step 2: Require approval on the environment

Environment administrator. Set the environment's Connect mode to by-request so no one reaches the cluster without an approved grant.

<Tabs>
  <Tab title="Dashboard">
    Go to **Environments** → **customer-prod** → **Settings** → **Ryvn Connect**, choose **By request only**, then click **Save Connect access**.
  </Tab>

  <Tab title="GitOps">
    Add the `connect` block to the environment you already declare, keeping the rest of the spec unchanged.

    ```yaml theme={null}
    # yaml-language-server: $schema=https://api.ryvn.app/v1/schemas/resources.json
    kind: Environment
    metadata:
      name: customer-prod
    spec:
      connect:
        mode: by-request
    ```
  </Tab>
</Tabs>

<Note>
  Two other modes exist. **Default access** (`mode: default` with a `defaultPermissionSet`) grants that one set without approval and follows its latest revision. **Off** (`mode: off`) blocks new access and revokes active grants. An environment that has never set `connect` keeps its previous role-derived behavior.
</Note>

## Step 3: Request access

Request 30 minutes of access and include the reason:

```bash theme={null}
ryvn connect customer-prod \
  --permission-set support-debug \
  --reason "Investigate elevated 5xx on api" \
  --duration 30m
```

```
Status:          approval_pending
Environment:     customer-prod
Permission set:  support-debug
Request:         550e8400-e29b-41d4-a716-446655440000

Resume with:
  ryvn connect customer-prod --request 550e8400-e29b-41d4-a716-446655440000
```

The command exits while approval is pending. `--reason` is required. `--duration` defaults to 1 hour and cannot exceed 8 hours. An undecided request expires after 24 hours, which is a deadline for the decision, not the length of the access.

While an identical request is still pending, running the same command again returns that request instead of creating a second one. Change the reason or duration and you create a new request.

Check on it without re-requesting:

```bash theme={null}
ryvn get access-request --status pending -e customer-prod
ryvn describe access-request 550e8400-e29b-41d4-a716-446655440000
```

## Step 4: Approve

Your customer approves in the dashboard **Inbox**, or under **Environment** → **Access requests** on their stack. **Pending only** lists what needs a decision; **All requests** and **View grants** show decisions already made.

Each request shows the requester, the environment, the permission set and its revision, the requested duration, and the reason. Expand it to read the rules before deciding. Approval accepts exactly what was requested; there is no field for trimming the namespace or dropping a verb. To narrow it, deny the request and ask for a different permission set.

<Tabs>
  <Tab title="Dashboard">
    Click **Approve**, or **Deny** with a note.
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    ryvn access-request approve 550e8400-e29b-41d4-a716-446655440000 \
      --reason "Approved for 5xx investigation"

    ryvn access-request deny 550e8400-e29b-41d4-a716-446655440000 \
      --reason "Use the read-only set for this"
    ```
  </Tab>
</Tabs>

Approval creates a grant pinned to that revision, and the 30 minutes start at approval rather than at your first `kubectl` command. A new request emits an `access_request.pending` notification to any [Slack destination](/docs/observability/notifications) subscribed to that event.

## Step 5: Connect and debug

Run the resume command the CLI printed. In the dashboard, use **Copy resume command** on your own pending request, or **Copy connect command** once it is approved. Both commands pin the org, so prefer them over retyping:

```bash theme={null}
ryvn connect customer-prod --request 550e8400-e29b-41d4-a716-446655440000
```

That opens a subshell wired to the environment. Append `-- <command>` to run one command and exit instead.

```bash theme={null}
kubectl -n app-system get pods
```

```
NAME                   READY   STATUS    RESTARTS   AGE
api-7d4f8b9c5-2xk4p    1/1     Running   0          4h12m
```

```bash theme={null}
kubectl -n app-system exec api-7d4f8b9c5-2xk4p -- \
  curl -s localhost:8080/debug/queue-depth
```

An interactive shell works the same way:

```bash theme={null}
kubectl -n app-system exec -it api-7d4f8b9c5-2xk4p -- /bin/sh
```

Pass `-n app-system` explicitly. Every request is authorized against the namespace it names, so a command in another namespace is denied, and so is a cluster-wide request such as `kubectl get pods -A`. A command without `-n` uses your current context's namespace, which is denied when that namespace is outside the grant.

<Warning>
  Exec runs with the container's privileges, including its mounted credentials. Leaving `secrets` out of the permission set stops Kubernetes Secrets API reads, not what a process in the pod can already reach.
</Warning>

Reconnect as often as you need while the grant lasts. A single exec or watch stream is capped at 15 minutes; run the command again for a new stream. Add `--kubeconfig` to write a context into `~/.kube/config` for your own tooling; it stops working when the grant ends. Only pod exec is bridged, so `kubectl attach` and `kubectl port-forward` are refused.

## Step 6: Revoke access

Revoke your own grant when you are finished. Your customer can revoke any grant on their environment.

```bash theme={null}
ryvn get access-grant --status active -e customer-prod
ryvn access-grant revoke <grant-id> --reason "Incident resolved"
```

In the dashboard, go to **Inbox** → **View grants** → **Revoke**. Open streams close as soon as authorization stops holding.

Review what happened afterwards:

```bash theme={null}
ryvn describe access-request <request-id>
ryvn describe access-grant <grant-id>
ryvn describe permission-set support-debug --revision <revision-id>
```

Ryvn records the request, the decision, the pinned rules, and the grant window. It does not record the commands run inside an exec session or their output.

Expired and revoked grants are final. To continue, submit a new request with a reason, which can describe the same incident.

## Troubleshooting

<AccordionGroup>
  <Accordion title="Status stays approval_pending">
    Nobody has decided yet, and re-running `ryvn connect` returns the same request. Ask a `developer` or `operator` on the environment other than yourself to decide. Undecided requests expire after 24 hours.
  </Accordion>

  <Accordion title="Approved, but connect returns materialization_pending">
    The grant is valid and the environment has not confirmed the revision on the cluster yet. The agent may be updating, disconnected, or running components that predate permission sets. Retry shortly; if it persists, ask the environment administrator to check the environment's deployed components. Readiness can also cause the server to refuse an approval.
  </Accordion>

  <Accordion title="Projection readiness says unavailable">
    Expected. The API does not expose per-environment readiness yet, so `describe permission-set` prints `unavailable` instead of guessing. Neither the CLI nor the dashboard previews which namespaces a revision currently covers.
  </Accordion>

  <Accordion title="kubectl access is not enabled for this environment">
    The environment has no access relay for Ryvn Connect, independent of permission sets. Ask the environment administrator to enable Connect for it.
  </Accordion>

  <Accordion title="This request is outside the permission set granted to your session">
    The verb, resource, and namespace of that one request are not in the revision your grant pinned. Common causes are a different namespace, a cluster-wide request, a subresource the rules omit such as `pods/log`, or exec rules missing a verb. Check the pinned revision with `ryvn describe permission-set support-debug --revision <revision-id>`. Editing the set does not change a grant already approved.
  </Accordion>

  <Accordion title="This session is not permitted to exec into pods">
    The set's `pods/exec` rule lacks `create`, the verb exec requests are authorized against. Publish a revision granting `create` on `pods/exec` and request again.
  </Accordion>

  <Accordion title="Exec requires WebSocket streaming">
    kubectl is negotiating SPDY. Upgrade to kubectl v1.31 or newer.
  </Accordion>

  <Accordion title="Namespace not found, or forbidden despite matching the name">
    A `namespaceNames` scope covers only namespaces Ryvn manages in that environment. Confirm the name with `ryvn describe installation api -e customer-prod`, and confirm the namespace is declared on the environment or created for an installation. Reserved namespaces are always refused.
  </Accordion>

  <Accordion title="Access is no longer authorized (grant_inactive)">
    The grant expired, was revoked, or the environment's Connect mode changed, including a switch to **Off**. Request access again.
  </Accordion>

  <Accordion title="Multiple grants, or the wrong org or profile">
    `multiple_grants` means more than one active grant applies; choose one with `ryvn connect customer-prod --grant <grant-id>`. If a permission set or environment appears not to exist, check `ryvn auth status` and pass `--profile` for the org that owns the environment.
  </Accordion>
</AccordionGroup>

## Related

* [Deployment Approvals](/docs/guides/deployment-approvals) for the same review model applied to changes
* [Access Policy](/docs/iac/access-policy) for the roles that decide who may request, approve, and revoke
* [Ryvn CLI](/docs/cli) for installation, profiles, and authentication
