api-and-namespace-design · git:20260512.41b1d03 · 2026-05-12 · sha256 9bb92d6b4db70c9e
api-and-namespace-design git:20260512.41b1d03A
Immutable. This exact content is served forever at /api/v1/blob/9bb92d6b4db70c9e.
---
name: api-and-namespace-design
description: API design conventions, namespace coordinate system, RBAC roles, ClawHub compatibility layer, OpenAPI contract sync rules, and CSRF/session handling.
license: Apache-2.0
---
# API and Namespace Design Skill
## Trigger
Use this skill when:
- Adding or modifying REST API endpoints
- Changing namespace, skill, or user coordinate logic
- Working on ClawHub CLI compatibility layer
- Modifying OpenAPI specifications or generated types
- Adding new admin or governance endpoints
## Namespace Coordinate System
SkillHub uses a two-axis coordinate model:
```
@{namespace_slug}/{skill_slug}
```
- `@global/my-skill` — Global namespace skill
- `@my-team/my-skill` — Team namespace skill (namespace slug is any valid slug)
- `@department-ops/my-skill` — Department namespace skill
### Namespace Model
Namespaces (`domain/namespace/`):
- **Slug**: unique identifier, validated by `SlugValidator`
- **Status**: `ACTIVE`, `FROZEN`, `ARCHIVED`
- **Roles**: `OWNER`, `ADMIN`, `MEMBER`
- Frozen or archived namespaces cannot publish skills
### RBAC Roles
**Namespace-level** (`domain/namespace/NamespaceRole`):
- `OWNER` — Full control over namespace and all skills
- `ADMIN` — Can manage members, archive skills, publish
- `MEMBER` — Can publish skills to the namespace
**Platform-level**:
- `SUPER_ADMIN` — Bypasses all permission checks, can publish directly without review
## ClawHub Compatibility Layer
ClawHub CLI uses a single-slug model (no `/` allowed in slugs). Mapping:
| SkillHub Coordinate | Canonical Slug | Notes |
|---------------------|----------------|-------|
| `@global/my-skill` | `my-skill` | Global namespace omits prefix |
| `@team-name/my-skill` | `team-name--my-skill` | Double-dash separator |
**Conflict resolution**: `--` split takes priority. `@global/team-name--my-skill` would conflict
with `@team-name/my-skill`, resolved to the team namespace skill. Global skill slugs must NOT
contain `--`.
## API Design
### Controllers
- Controllers in `skillhub-app` (`com.iflytek.skillhub.controller/`) are **transport only**
- Responsibilities: extract auth context, bind request params, wrap responses
- Complex business logic belongs in domain services (`skillhub-domain`) or app services
- Use Springdoc OpenAPI annotations (`@Operation`, `@ApiResponse`) for API documentation
- User identity is always **String** in API inputs and outputs
### Request/Response Patterns
- DTOs in `com.iflytek.skillhub.dto/`
- `ReviewTaskRequest` / `ReviewTaskResponse` for review workflow
- Response wrapping handled at controller layer
- Validation errors use `DomainBadRequestException` with i18n message keys
### Session and CSRF
- Session-based auth with cookie storage
- CSRF protection via `XSRF-TOKEN` cookie and `X-XSRF-TOKEN` header
- Smoke tests validate the full register → login → CSRF → action → logout flow
- Mock auth uses `X-Mock-User-Id` header in local dev
### Well-known Discovery
`/.well-known/clawhub.json` returns `{ "apiBase": "/api/v1" }` for ClawHub CLI auto-discovery.
## OpenAPI Contract Sync
When backend API contracts change:
```bash
make generate-api
```
This runs `openapi-typescript http://localhost:8080/v3/api-docs -o src/api/generated/schema.d.ts`.
Commit the updated `web/src/api/generated/schema.d.ts` with the PR.
To verify no drift:
```bash
./scripts/check-openapi-generated.sh
```
This starts local dependencies, boots the backend, regenerates the schema, and fails if the
checked-in SDK is stale.
## Versioning and Tags
- Semantic versioning for skill versions (`major.minor.patch`)
- `latest` tag is system-reserved, read-only, auto-follows `Skill.latestVersionId`
- Custom tags (`stable`, `beta`) are manually maintained
- `latest` cannot be moved manually
- Auto-generated versions use `yyyyMMdd.HHmmss` format when no version is specified in SKILL.md
## Key API Endpoints
| Method | Path | Purpose |
|--------|------|---------|
| `GET` | `/api/v1/auth/me` | Current user info (401 if unauthenticated) |
| `POST` | `/api/v1/auth/local/login` | Local account login |
| `POST` | `/api/v1/auth/local/register` | Local account registration |
| `POST` | `/api/v1/auth/logout` | Logout (302/200/204) |
| `POST` | `/api/v1/auth/local/change-password` | Password change |
| `GET` | `/api/v1/namespaces` | List namespaces |
| `GET` | `/api/v1/labels` | List visible labels (public) |
| `POST` | `/api/v1/admin/labels` | Create label definition (admin) |
| `DELETE` | `/api/v1/admin/labels/{slug}` | Delete label definition (admin) |
| `GET` | `/actuator/health` | Health check |
| `GET` | `/actuator/prometheus` | Prometheus metrics |
## Common Pitfalls
- Forgetting CSRF token on POST/PUT/DELETE requests (needs `X-XSRF-TOKEN` header)
- Using numeric user IDs in API — all user identities are **String**
- Not regenerating OpenAPI types after adding/changing endpoints
- Putting business logic in controllers instead of domain/app services
- Assuming namespace slugs follow a specific prefix pattern — they are arbitrary valid slugs