Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 7 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ polymorfa session status SESSION_ID --json
Use a session ID from the list for `view` or `status`; these commands do not
accept `--project`. `status` reads the stored status and optional reason from
the session record. It does not probe the device or wait for a connection.
Key metadata listing and revocation are available through `key list` and
`key revoke`; the CLI does not mint or rotate organization keys.
Create, list and revoke organization keys and project tokens as a signed-in
person in the Console. These operations are not available through the CLI.

## QuickLinks

Expand Down Expand Up @@ -1580,19 +1580,16 @@ request metadata.

## Platform access and security

Organization server API keys can inspect the credential, membership, audit,
ban, incident, saved QuickLink settings, and project-token metadata made
Organization server API keys can inspect the membership, audit,
ban, incident and saved QuickLink settings made
available by the Platform API:

```shell
polymorfa key list
polymorfa key revoke key_123 --yes --idempotency-key revoke_key_123
polymorfa member list
polymorfa audit-log list --action key.revoked --resource api-key --limit 100
polymorfa session-ban list --active
polymorfa security-incident list
polymorfa security-incident acknowledge incident_123
polymorfa project-token list --project project_123
polymorfa widget view --project project_123
polymorfa widget update --project project_123 --input widget-patch.json
polymorfa session stop session_1 session_2 --project project_123
Expand All @@ -1611,16 +1608,11 @@ apply to this kind of account, for example a business-only feature on a
personal number). Before the number's first sync every value is `unknown`.
It is a beta: the team must be enrolled, and it needs `sessions:read`.

Key and project-token lists contain metadata only; neither command returns raw
bearer material. The API's reported API-key scope mask is retained in JSON but
must not be treated as proof of live authorization. `key revoke` requires
`--yes` because revocation is immediate. If a successful response is lost, a
retry can return not found because the API does not persist idempotency keys for
this route. Security-incident acknowledgement is repeatable and sends no body.
Security-incident acknowledgement is repeatable and sends no body.

Audit filters are exact matches, and `--limit` is bounded to 1 through 500.
These list operations return complete arrays and expose no cursor. Project-token
listing requires one explicit project ID. Follow asynchronous work with the
These list operations return complete arrays and expose no cursor.
Follow asynchronous work with the
`operation` commands described in [Operations](#operations).

Member invite, role update, and removal remain dashboard-session routes and are
Expand Down
29 changes: 6 additions & 23 deletions docs/command-research/platform-management.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Platform management command research

- **Status:** Organization-key management commands reconciled with the unified
client; operation inspection is console-only
- **Reviewed:** 2026-09-16
- **Status:** Public organization management commands; credential management is Console-only; operation inspection is console-only
- **Reviewed:** 2026-10-17
- **SDK baseline:** `08452992a32a392de5fc3d0fa3954ba6addd7f32`

## Sources reviewed
Expand All @@ -28,10 +27,6 @@
## Shipped command tree

```text
key
list
revoke <key> --yes

member
list

Expand All @@ -48,38 +43,30 @@ security-incident
operation
list|view|wait|watch|cancel --owner <project|organization>

project-token
list --project <project>

session
stop <session...> [--project <project>]
delete <session...> [--project <project>] --yes
```

The management commands use the public typed organization resources on the
unified SDK `Client`. They require an organization server API key. Reads require
`sessions:read`; key revocation and incident acknowledgement require
`sessions:read`; incident acknowledgement requires
`sessions:manage`; batch session stop/delete also require `sessions:manage`.
Project tokens and Messaging browser client tokens are rejected before
transport. Developer operations separately accept the principal allowed by
their explicit owner and context.

## Command decisions

The API method is named `deactivate`; the CLI uses `key revoke` because it
describes the security outcome and matches established credential-management
language. It requires `--yes` and names the key ID. An optional idempotency key
is passed through, but the API does not persist it: retrying after an unseen
success can return not found. JSON preserves `scopes` as reported metadata but
neither human output nor documentation presents that mask as proof of current
authorization.
Organization keys and project tokens can only be created, listed and revoked by
a signed-in person in the Console. No machine credential command manages them.

Member access is deliberately list-only. Invite, role update, and removal use
dashboard sessions and are not crossed through `raw.request`.

Audit action and resource filters are exact matches. The CLI validates the
live handler's 1..500 limit even though the pinned OpenAPI omits those query
fields. Audit, member, ban, incident, key, and token results are complete arrays
fields. Audit, member, ban and incident results are complete arrays
without cursor semantics.

`session-ban list --active` keeps one discovery command while selecting the
Expand All @@ -91,10 +78,6 @@ The original singular Platform operation view was removed. Operation
inspection is console-only at API contract 1600403, and the pinned SDK has no
operations resource, so the CLI ships no operation commands. The unpublished
`--surface` selector was removed.
Project-token listing requires an explicit project because the live
organization-key handler rejects an absent project even though its OpenAPI
query field is optional. Only metadata is returned.

Batch session commands use positional IDs because they are the primary action
targets. The CLI trims and validates 1..100 unique IDs before transport instead
of relying on the handler's silent deduplication. `session stop` reports the
Expand Down
5 changes: 5 additions & 0 deletions docs/superpowers/specs/2026-08-19-polymorfa-cli-design.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
> Amendment (2026-10-17): organization-key listing/revocation and project-token
> listing are removed. Only signed-in Console people manage those credentials.
> Client-token routes remain available. This supersedes credential-management
> entries in the historical design and implementation record below.

# Polymorfa CLI design

- **Status:** Living design; the accepted 2026-08-20 developer API RFC supersedes
Expand Down
6 changes: 0 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,9 +174,6 @@
"identity": {
"description": "Resolve phone numbers and usernames to Polymorfa user IDs."
},
"key": {
"description": "Inspect and revoke organization API keys."
},
"label": {
"description": "Manage Messaging labels and chat assignments."
},
Expand Down Expand Up @@ -228,9 +225,6 @@
"project": {
"description": "Select, list, and inspect the project context."
},
"project-token": {
"description": "Inspect project-token metadata."
},
"quick-reply": {
"description": "Manage observed business quick replies."
},
Expand Down
13 changes: 0 additions & 13 deletions src/commands/key/list.ts

This file was deleted.

35 changes: 0 additions & 35 deletions src/commands/key/revoke.ts

This file was deleted.

27 changes: 0 additions & 27 deletions src/commands/project-token/list.ts

This file was deleted.

55 changes: 0 additions & 55 deletions src/services/platform-management.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import {
type ApiKey,
type ApiKeyDeactivation,
type AuditLog,
type ClientInstance,
type ListAuditLogsParams,
type OrganizationMember,
PolymorfaValidationError,
type ProjectToken,
type RequestOptions,
type ResponseMetadata,
type SecurityIncident,
Expand Down Expand Up @@ -35,22 +32,6 @@ export interface PlatformResult<T> {
}
}

export async function listApiKeys(
client: ClientInstance<'organization'>,
): Promise<PlatformResult<readonly ApiKey[]>> {
const response = await client.apiKeys.list()
return result(response.data.data, response.metadata)
}

export async function revokeApiKey(
client: ClientInstance<'organization'>,
keyId: string,
options: RequestOptions = {},
): Promise<PlatformResult<ApiKeyDeactivation>> {
const response = await client.apiKeys.deactivate(keyId, options)
return result(response.data.data, response.metadata)
}

export async function listMembers(
client: ClientInstance<'organization'>,
): Promise<PlatformResult<readonly OrganizationMember[]>> {
Expand Down Expand Up @@ -95,14 +76,6 @@ export async function acknowledgeSecurityIncident(
return result(response.data.data, response.metadata)
}

export async function listProjectTokens(
client: ClientInstance<'organization'>,
projectId: string,
): Promise<PlatformResult<readonly ProjectToken[]>> {
const response = await client.projectTokens.list(projectId)
return result(response.data.data, response.metadata)
}

export async function listManagedSessions(
client: ClientInstance<'organization'>,
projectId?: string,
Expand Down Expand Up @@ -234,20 +207,6 @@ export async function deleteSessions(
return result(response.data.data, response.metadata)
}

export function renderApiKeys(keys: readonly ApiKey[]): string {
if (keys.length === 0) return 'No API keys found.'
return keys
.map((key) =>
[
key.keyId,
key.label,
`••••${key.last4}`,
key.isActive ? 'active' : 'inactive',
].join('\t'),
)
.join('\n')
}

export function renderMembers(members: readonly OrganizationMember[]): string {
if (members.length === 0) return 'No organization members found.'
return members
Expand Down Expand Up @@ -303,20 +262,6 @@ export function renderSecurityIncidents(
.join('\n')
}

export function renderProjectTokens(tokens: readonly ProjectToken[]): string {
if (tokens.length === 0) return 'No project tokens found.'
return tokens
.map((token) =>
[
token.id,
token.label ?? '',
`${token.start}••••${token.last4}`,
token.revokedAt === null ? 'active' : 'revoked',
].join('\t'),
)
.join('\n')
}

export async function viewWidgetSettings(
client: ClientInstance<'organization'>,
projectId?: string,
Expand Down
Loading