<!-- task=m-principal-provider-credential-slot-routing/05+01,02_management_ops plan=1 tag=API -->

# Credential Management Core Foundation

## For the Implementing Agent

Filling implementation-owned sections of `CODE_REVIEW-cloud-G08.md` is mandatory. Run every verification command, paste actual output, keep active files in place, and report ready for review; only code-review may finalize, rename logs, write `complete.log`, or archive. If blocked, record exact blocker evidence and resume conditions only in implementation-owned fields; do not ask the user, call user-input tools, create stop files, or classify next state.

## Background

Control Plane needs a principal-scoped management service and a host-local first-principal bootstrap command. The current `/client` WebSocket is plaintext and unauthenticated, while the SDD explicitly forbids credential-bearing operations on it; this packet builds the service/CLI core but does not expose a remote management operation or claim S08 completion.

## Archive Evidence Snapshot

- Prior pair: `agent-task/m-principal-provider-credential-slot-routing/05+01,02_management_ops/plan_cloud_G10_0.log`, `agent-task/m-principal-provider-credential-slot-routing/05+01,02_management_ops/code_review_cloud_G10_0.log`.
- Verdict/evidence: implementation had not started; no verdict or verification output.
- Replan reason: the previous plan sent raw IOP/provider credentials on the current plaintext Client WebSocket and implemented part of Epic-3 at-rest protection while claiming only S08.
- Roadmap carryover: this is a preparatory service/CLI packet. PASS must not check `credential-management`; S08 requires S09 at-rest sealer and S10 confidential Client transport rollout.

## Analysis

### Files Read

- `apps/control-plane/cmd/control-plane/main.go`
- `apps/control-plane/cmd/control-plane/server.go`
- `apps/control-plane/cmd/control-plane/config_test.go`
- `apps/control-plane/internal/wire/client.go`
- `apps/control-plane/internal/wire/client_test.go`
- `proto/iop/control.proto`
- `apps/client/lib/iop_wire/client_wire_client.dart`
- `apps/client/lib/iop_wire/parser_map.dart`
- `apps/client/test/iop_wire/client_wire_client_test.dart`
- `apps/client/test/iop_wire/parser_map_test.dart`
- `agent-contract/inner/client-control-plane-wire.md`

### SDD Criteria

- SDD: `agent-roadmap/sdd/operational-observability-provider-management/principal-provider-credential-slot-routing/SDD.md` (`승인`, 잠금 해제).
- Foundation maps to S08 / `credential-management`, Evidence Map S04-S08, but intentionally lacks the confidential remote operation required for full S08.
- Checklist covers host-local-only bootstrap, one-time raw token output, principal/revision authorization service fixtures, secret-blind list responses, and structural proof that no bootstrap/management protobuf or ClientServer listener is introduced.

### Verification Context

- handoff 없음. SDD says principal clients authenticate with IOP token over TLS and current plain WS cannot carry credential-bearing operations.
- Go 1.26.2 linux/arm64. Tests use temp SQLite, fake sealer, injected logger/random reader, and workspace-local Go temp/cache paths.
- Preconditions 01 and 02 are currently incomplete; runtime resolves their archived completion artifacts.
- No external key manager, TLS runner, Flutter change, or provider is required. Confidence: high for core foundation.

### Test Coverage Gaps

- CLI exposes only `serve`; no bootstrap/idempotency/one-time-output test exists.
- No principal/revision authorization service exists.
- Existing ClientServer tests cover hello only; no test currently protects the intentional absence of credential-bearing listeners.

### Symbol References

- Rename/remove 없음.
- `rootCmd` adds commands at `main.go:61`; `serveCmd` remains unchanged.
- `wire.NewClientServer` and Dart client/parser call sites are intentionally unchanged.

### Split Judgment

- The service authorization/CAS contract and host-local bootstrap are a stable useful foundation that preserves current network behavior and independently PASSes.
- Predecessors 01 and 02: active/archived completion missing, ambiguity 없음.
- Remote Client operation and concrete `SecretSealer` are excluded and must be planned with S09/S10; no Roadmap Targets section is present.

### Scope Rationale

- Do not modify `proto/iop/control.proto`, ClientServer, Dart client/parser, configs, or Control Plane server wiring.
- Define a `SecretSealer` interface only; tests use a fake. Do not add AES/key-file/deployment-key implementation in this packet.
- Do not add remote bootstrap, credential reveal, or plaintext network payloads.

### Final Routing

- `status=routed`, `evaluation_mode=isolated-reassessment`, finalizer=`finalize-task-policy.sh`, pair mode.
- Build closures all true for core foundation; scores `2+2+1+1+1=G07`; base/route=`local-fit`, local, `PLAN-local-G07.md`.
- Review closures all true; scores `2+2+1+1+2=G08`; `official-review`, cloud, `gpt-5.6-sol` xhigh, `CODE_REVIEW-cloud-G08.md`.
- `large_indivisible_context=false`; risks=`temporal_state`, `concurrent_consistency`, `boundary_contract` (3); rework=0; integrity failure=false; no capability gap.

## Dependencies and Execution Order

1. `01_principal_store` and `02+01_credential_catalog` must complete; runtime accepts their archived `complete.log` files.
2. Implement authorization/CAS service, then host-local bootstrap and absence guards.
3. No dependency absent from `05+01,02_management_ops` may be introduced.

## Implementation Checklist

- [ ] Add an in-process principal-authenticated management service with revision authorization, secret-blind records, and an injected-but-unimplemented `SecretSealer` boundary.
- [ ] Add a host-local-only first-principal/bootstrap CLI that emits the initial raw IOP token exactly once and never logs or persists it.
- [ ] Prove own-principal lifecycle, cross-principal denial, stale revision conflict, one-time output, restart persistence, and raw-secret redaction with deterministic tests.
- [ ] Prove no management/bootstrap protobuf, ClientServer listener, Dart method, or plaintext network activation was added; leave S08 completion for S09/S10 rollout.
- [ ] Run fresh race, Control Plane regression, archive-predecessor, structural absence, and diff verification.
- [ ] Fill implementation-owned sections in CODE_REVIEW-*-G??.md with actual implementation notes and verification output.

### [API-1] Add the in-process management core

**Problem:** `apps/control-plane/internal/wire/client.go:62-73` has only hello, and there is no reusable service enforcing principal identity, target ownership, expected revision, or secret-blind response types.

**Solution:** Add `credentialops.Service` over the store. It hashes the presented IOP token in process, resolves the principal, authorizes target equality, maps CAS/lifecycle errors, and exposes token/slot/route operations. Provider create/rotate receives plaintext only at the method boundary, requires an injected `SecretSealer`, clears the local byte slice on return, and passes only an opaque envelope to the store. The production packet supplies no sealer or network adapter.

Before (`apps/control-plane/internal/wire/client.go:62-73`):

```go
proto_socket.AddRequestListenerTyped(&client.Communicator, func(req *iop.ClientHelloRequest) (*iop.ClientHelloResponse, error) {
    logger.Info("client hello request received",
        zap.String("client_id", req.ClientId),
        zap.String("client_version", req.ClientVersion),
    )
    return &iop.ClientHelloResponse{
        Ready:              true,
        Protocol:           Protocol,
        ServerTimeUnixNano: time.Now().UnixNano(),
        Message:            "Hello from IOP Control Plane",
    }, nil
})
```

After service-only boundary:

```go
import (
    "context"

    "iop/apps/control-plane/internal/credentialstore"
)

type SecretSealer interface {
    Seal(context.Context, []byte, SecretContext) (credentialstore.EncryptedSecretEnvelope, error)
}
func (s *Service) RotateSlot(ctx context.Context, rawIOPToken []byte, input RotateSlotInput) (SlotRecord, error)
```

**Modified Files and Checklist:**

- [ ] `apps/control-plane/internal/credentialops/service.go`: implement authentication, ownership, CAS, lifecycle mapping, secret-blind DTOs, and sealer interface.
- [ ] `apps/control-plane/internal/credentialops/service_test.go`: cover own/cross-principal operations, stale revision, lifecycle, missing sealer, fake-sealer ciphertext handoff, and captured-log redaction.

**Test Strategy:** Write `TestServiceScopesOperationsToAuthenticatedPrincipal`, `TestServiceRejectsCrossPrincipalTargets`, `TestServiceRejectsStaleRevision`, `TestServiceRequiresSealerForSecretMutation`, and `TestServiceResponsesAreSecretBlind` using temp store and deterministic fake sealer.

**Verification:** `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go test -count=1 -race ./apps/control-plane/internal/credentialops` passes.

### [API-2] Add host-local first-principal bootstrap

**Problem:** `apps/control-plane/cmd/control-plane/main.go:59-62` registers only `serve`, leaving no deployment-admin bootstrap path.

**Solution:** Add `principal bootstrap --alias` that loads config, opens the DB directly, refuses when any principal already exists, issues the first token through packet-01 store, and writes the raw token once to command stdout without zap/error inclusion. No remote operation is added.

Before (`apps/control-plane/cmd/control-plane/main.go:59-62`):

```go
}
root.PersistentFlags().StringVarP(&cfgFile, "config", "c", "configs/control-plane.yaml", "config file path")
root.AddCommand(serveCmd())
return root
```

After:

```go
root.PersistentFlags().StringVarP(&cfgFile, "config", "c", "configs/control-plane.yaml", "config file path")
root.AddCommand(serveCmd(), principalCmd())
return root
```

**Modified Files and Checklist:**

- [ ] `apps/control-plane/cmd/control-plane/main.go`: register the host-local principal command.
- [ ] `apps/control-plane/cmd/control-plane/credential_commands.go`: implement direct DB bootstrap with explicit stdout/stderr writers.
- [ ] `apps/control-plane/cmd/control-plane/credential_commands_test.go`: test first bootstrap, second refusal, restart, one-time stdout, and log/error redaction.

**Test Strategy:** Write `TestPrincipalBootstrapEmitsTokenOnce`, `TestPrincipalBootstrapRefusesExistingPrincipal`, `TestPrincipalBootstrapPersistsAcrossReopen`, and `TestBootstrapLogsNeverContainToken`.

**Verification:** `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go test -count=1 ./apps/control-plane/cmd/control-plane -run 'PrincipalBootstrap|Credential'` passes.

### [API-3] Guard the secure-transport boundary

**Problem:** a future implementer could expose the service on current plaintext ClientServer and accidentally create the SDD-forbidden credential path.

**Solution:** Add source-level absence tests that fail if bootstrap/credential protobuf messages or ClientServer credential listeners appear in this packet. Document the deferred adapter in service comments and tests, not the active wire contract.

Before (`proto/iop/control.proto`, current client baseline):

```protobuf
message ClientHelloRequest {
  string client_id = 1;
  string client_version = 2;
}
```

After:

```text
unchanged: no BootstrapRequest, CredentialOperationRequest, or raw token field
```

**Modified Files and Checklist:**

- [ ] `apps/control-plane/internal/credentialops/service_test.go`: assert the service has no network registration side effect and missing sealer fails closed.
- [ ] `apps/control-plane/cmd/control-plane/credential_commands_test.go`: inspect root commands and prove bootstrap is CLI-only.

**Test Strategy:** Use deterministic `rg` final verification plus Go tests; no wire/Dart tests are changed because their contract remains untouched.

**Verification:** `if rg --sort path -n 'message[[:space:]]+.*(Bootstrap|CredentialOperation)|AddRequestListenerTyped.*Credential' proto/iop/control.proto apps/control-plane/internal/wire/client.go; then exit 1; else exit 0; fi` passes with no matches.

## Modified Files Summary

| File | Items |
|------|-------|
| `apps/control-plane/internal/credentialops/service.go` | API-1 |
| `apps/control-plane/internal/credentialops/service_test.go` | API-1, API-3 |
| `apps/control-plane/cmd/control-plane/main.go` | API-2 |
| `apps/control-plane/cmd/control-plane/credential_commands.go` | API-2 |
| `apps/control-plane/cmd/control-plane/credential_commands_test.go` | API-2, API-3 |
| `agent-task/m-principal-provider-credential-slot-routing/05+01,02_management_ops/CODE_REVIEW-cloud-G08.md` | API-1, API-2, API-3 |

## Final Verification

Run from `/config/workspace/iop-s0`; fresh Go results are required.

1. `test "$(find agent-task/archive -type f -path '*/m-principal-provider-credential-slot-routing/01_principal_store/complete.log' -print | wc -l)" -eq 1 && test "$(find agent-task/archive -type f -path '*/m-principal-provider-credential-slot-routing/02+01_credential_catalog/complete.log' -print | wc -l)" -eq 1` — both archived predecessors exist exactly once.
2. `mkdir -p .cache/go-build .cache/go-cache` — executable Go paths exist.
3. `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go vet ./apps/control-plane/internal/credentialops ./apps/control-plane/cmd/control-plane` — no vet findings.
4. `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go test -count=1 -race ./apps/control-plane/internal/credentialops ./apps/control-plane/cmd/control-plane` — service/CLI tests and races pass.
5. `GOTMPDIR=/config/workspace/iop-s0/.cache/go-build GOCACHE=/config/workspace/iop-s0/.cache/go-cache go test -count=1 ./apps/control-plane/...` — Control Plane regression suite passes.
6. `if rg --sort path -n 'message[[:space:]]+.*(Bootstrap|CredentialOperation)|AddRequestListenerTyped.*Credential' proto/iop/control.proto apps/control-plane/internal/wire/client.go; then exit 1; else exit 0; fi` — no plaintext management wire was added.
7. `git diff --check` — no whitespace errors.

After completing all code changes, fill implementation-owned sections in `CODE_REVIEW-*-G??.md`.
