# Design: cp9 Account Persona Dogfood

Issue: <https://github.com/tidbcloud/controlplane9/issues/254>

Design:
[`20260527-design-cp9-account-persona.md`](./20260527-design-cp9-account-persona.md)

This is the dogfood design and manual operator contract for the Account persona. Implementation
PRs must replace Phase 0 placeholders with concrete commands and prove this
flow repeatedly before the Account side of #254 can close.

## Finish Line

1. Start cp9 with the `account` persona.
2. Run real Account, Auth Server, and API-gateway source services through Tilt.
3. Create or validate a local identity/RBAC/API-key/token primitive.
4. Authorize a TiDB Cloud API request through Kong/API-gateway and
   Account/Auth logic.
5. Prove the matching negative authorization case.
6. Prove the selected MySQL, DynamoDB, and Redis storage/cache path.
7. Hot reload an Account/Auth/API-gateway source edit in the same session.
8. Clean up local/AWS resources created by the run.

## Prerequisites

Use the current cp9 command surface:

```bash
bin/cp9 setup config=cp9.yaml
bin/cp9 doctor config=cp9.yaml
bin/cp9 up dry-run=true config=cp9.yaml
bin/cp9 up config=cp9.yaml
bin/cp9 status config=cp9.yaml
bin/cp9 logs config=cp9.yaml service=<service>
bin/cp9 destroy config=cp9.yaml
```

Deprecated helper commands are not part of this workflow.

Required local tools:

- `git`
- `go`
- `docker`
- `k3d`
- `kubectl`
- `helm`
- `tilt`
- `aws`
- `jq`
- `rg`
- `redis-cli` if the implementation exposes raw Redis inspection

Authenticate AWS only when the selected Account proof creates cloud resources:

```bash
aws sso login --profile sso
aws configure export-credentials --profile sso --format process >/dev/null
```

## Checkout Layout

Recommended sibling layout:

```text
work/
  controlplane9/
  account/
  auth-server/
```

Refresh heads and verify clean trees:

```bash
cd ~/work/controlplane9 && git fetch origin main && git checkout main && git pull --ff-only && git status --short
cd ~/work/account && git fetch origin main && git checkout main && git pull --ff-only && git status --short
cd ~/work/auth-server && git fetch origin main && git checkout main && git pull --ff-only && git status --short
```

Record SHAs before proof:

```bash
git -C ~/work/controlplane9 rev-parse HEAD
git -C ~/work/account rev-parse HEAD
git -C ~/work/auth-server rev-parse HEAD
```

## Create Config

```bash
cd ~/work/controlplane9
cp cp9.account.example.yaml cp9.yaml
```

The source example is `cp9.account.example.yaml`.

Edit at least:

- `name`: unique session, for example `cp9-<yourname>-account-1`;
- `tilt.port`: unused local port;
- `services.source.*.repo`: local Account/Auth/API-gateway checkout paths;
- owner/session/account/region/TTL fields;
- any local Redis/DynamoDB/MySQL settings the implementation exposes.

Keep staging as seed/config input only. Do not point runtime dependencies at
staging RDS, Redis, DynamoDB, Kong, Auth0, or email providers.

Keep `tidb-management-service`, `cluster-service-ng`, and `config-center`
stub-only for Account. If a selected Account/Auth/API-gateway path unexpectedly
needs one of them, narrow that dependency to a local stub or choose a smaller
Account-owned proof path; do not add those services as Account source/live
services.

## Preflight

```bash
bin/cp9 setup config=cp9.yaml
bin/cp9 doctor config=cp9.yaml
bin/cp9 up dry-run=true config=cp9.yaml
```

Before `up`, inspect the generated plan once the implementation exists:

```bash
SESSION=cp9-<yourname>-account-1
TILT_DIR=.cp9/$SESSION/tilt

rg 'account-central|account-openapi|account-provider|account-acl|account-worker|account-admin' "$TILT_DIR/Tiltfile" "$TILT_DIR/inputs"
rg 'authserver-central|authserver-provider|auth-server' "$TILT_DIR/Tiltfile" "$TILT_DIR/inputs"
rg 'kong|api-gateway|digest-auth|custom-basic-auth|bearer-token-auth|rate-limit|ddbtoredis' "$TILT_DIR/Tiltfile" "$TILT_DIR/inputs"
rg 'redis|dynamodb|ddb|mysql' "$TILT_DIR/inputs"
! rg 'tidb-management-service|cluster-service-ng|config-center-regional' "$TILT_DIR/Tiltfile"
! rg 'permissive-auth|auth-bypass|skip-auth' "$TILT_DIR"
! rg 'go-build-overlay|go-build-patches|go build -overlay' "$TILT_DIR"
! rg 'staging|prod-ms|staging-ms' "$TILT_DIR/inputs"
```

## Start

```bash
bin/cp9 up config=cp9.yaml
bin/cp9 status config=cp9.yaml
kubectl --kubeconfig .cp9/$SESSION/kubeconfig.yaml get pods -A
```

Open Tilt if needed:

```bash
open http://127.0.0.1:<tilt.port>
```

## Account Product Proof

The first Account proof is the real TiDB Cloud IAM API-key path from
`GET /v1beta1/apikeys`. It must go through local Kong/API-gateway and then into
`account-openapi`; direct calls to Account gRPC or HTTP services do not count.
The deterministic local proof key is:

- public/access key: `CP9IAMAK`
- private key for `curl --digest`: `123456`
- org id carried by Account Provider: `1440002`

```bash
# Expose the local Kong HTTPS proxy. Keep this running in one terminal.
kubectl -n default port-forward svc/kong-ingress-controller-kong-proxy 8443:443

# Positive: real IAM OpenAPI through Kong digest auth.
curl -kfsS --digest --user 'CP9IAMAK:123456' \
  'https://127.0.0.1:8443/v1beta1/apikeys?pageSize=3' | jq .

# Negative: wrong private key must fail at the gateway auth layer.
curl -ksS -o /tmp/cp9-account-iam-denied.json -w '%{http_code}\n' \
  --digest --user 'CP9IAMAK:wrong-private-key' \
  'https://127.0.0.1:8443/v1beta1/apikeys?pageSize=3'
cat /tmp/cp9-account-iam-denied.json | jq .

# Negative: missing public key must fail at the gateway auth layer.
curl -ksS -o /tmp/cp9-account-iam-missing-key.json -w '%{http_code}\n' \
  --digest --user 'CP9MISSING:123456' \
  'https://127.0.0.1:8443/v1beta1/apikeys?pageSize=3'
cat /tmp/cp9-account-iam-missing-key.json | jq .
```

The expected positive response is a JSON object with `apiKeys`; it must include
`accessKey: "CP9IAMAK"`. The negative calls should not reach Account business
logic as an authorized request.

## Storage And Cache Proof

```bash
# MySQL: Account Provider can resolve the same access key and org role binding.
kubectl exec deploy/account-metadb -- \
  mysql -uroot -pcp9-local account -e \
  "select id, access_key, status, type from uni_api_keys where access_key='CP9IAMAK';
   select target_type, target_id, scope_type, scope_id, role, status from role_bindings where target_id=9001;"

# RBAC: org owner has the selected IAM OpenAPI privileges.
kubectl exec deploy/account-metadb -- \
  mysql -uroot -pcp9-local account -e \
  "select rr.name, rp.resource_uri
     from rbac_roles rr
     join rbac_role_privileges rrp on rr.id=rrp.rbac_role_id
     join rbac_privileges rp on rp.id=rrp.rbac_privilege_id
    where rr.name='org:owner'
    order by rp.resource_uri;"

# Redis gateway cache: Kong lookup and rate-limit keys exist when expected.
kubectl exec deploy/account-redis -- \
  redis-cli -n 0 get 'dbaas:account:api_keys:CP9IAMAK'
kubectl exec deploy/account-redis -- \
  redis-cli -n 1 scan 0 match 'ratelimit:CP9IAMAK:*'
```

DynamoDB-local remains part of the Account local infra graph for the later API-key
create/update lane. This first read-only IAM proof intentionally seeds the
gateway Redis contract directly so it can prove the official IAM OpenAPI path
without relying on staging DynamoDB Streams.

## Hot Reload Proof

```bash
# Make a tiny Account/Auth/API-gateway source edit with observable local behavior.
tilt trigger account-openapi
tilt trigger account-provider
tilt trigger authserver-central
tilt trigger kong-ingress-controller
bin/cp9 logs config=cp9.yaml service=account-openapi since=5m
bin/cp9 logs config=cp9.yaml service=kong-ingress-controller since=5m

# Re-run the positive and negative authorization checks in the same session.
curl -kfsS --digest --user 'CP9IAMAK:123456' \
  'https://127.0.0.1:8443/v1beta1/apikeys?pageSize=3' | jq .
curl -sS -o /tmp/cp9-account-denied.json -w '%{http_code}\n' \
  --digest --user 'CP9IAMAK:wrong-private-key' \
  'https://127.0.0.1:8443/v1beta1/apikeys?pageSize=3'
```

## Repeatability

Before closing the Account implementation, run two fresh sessions from clean
heads with no live patches:

```bash
bin/cp9 destroy config=cp9.yaml
bin/cp9 setup config=cp9.yaml
bin/cp9 doctor config=cp9.yaml
bin/cp9 up config=cp9.yaml
```

## Cleanup

```bash
bin/cp9 destroy config=cp9.yaml
```

If the run created cloud resources, verify the current cp9 cleanup contract:
EKS, VPC graph, active instances, volumes, ENIs, LBs/TGs, S3, Route53, and any
other cp9-owned Account/API-gateway resources must be zero or not found for the
exact owner, session, account, and region.
