* fix(api): tell callers why a request was forbidden
The scope filter already computes an exact reason ("Missing API token
scope: skill:delete", "API token cannot access endpoint: /x") and the
access-denied handler discarded it, returning a bare "Forbidden" for
every case: missing scope, endpoint closed to API tokens, and paths
that simply don't exist. Clients cannot tell those apart, so they
guess — the published CLI reports every 403 as "token may lack
required scope", which sent us debugging token scopes for an hour when
the real causes were a revoked token and a mistyped namespace path.
The reason now rides in the response via a new error.forbidden.detail
message (en + zh), and is logged alongside the exception type.
Signed-off-by: Gal Eyal <gal.e@popai.health>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(api): safely expose API token denial reasons
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
---------
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* docs(faq): add community-sourced deployment and operations Q&A
Adds entries collected from real user-support threads to the reference FAQ
(both zh and en):
- 502 on auth APIs while the page loads, traced to server startup failure
on the SKILLHUB_DOWNLOAD_ANON_COOKIE_SECRET placeholder guard
- config changes not taking effect (.env.release.example vs .env.release,
restart vs recreate)
- built-in skill sync failure in offline environments
- upgrade path with Flyway auto-migration and volume retention
- external dependencies and the lack of MySQL support
- granting SUPER_ADMIN to an OAuth account via the bootstrap admin
- telling CLI and server versions apart
- installing skills into a target directory on an intranet
Signed-off-by: FenjuFu <fufenjupku@gmail.com>
* docs(faq): move entries to the published docs source and fix inaccuracies
Move the new FAQ entries from document/ (a generated tree that the docs
build does not read) to docs/skillhub/, which is what make docs-build and
the Pages deploy actually publish.
Also address review feedback:
- drop the SKILLHUB_BUILTIN_SKILLS_ENABLED tip; compose.release.yml does
not pass that variable through, so setting it has no effect
- correct the dependency list: object storage defaults to local, S3 is
recommended for production
- soften the 502 wording, since upstream/DNS/network can also cause it
- state the 32-character minimum for the cookie secret
- give a real bulk-install example and qualify v0.2.12 as a server version
- drop entries already covered by existing upgrade/MySQL/version questions
Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
* docs(faq): correct deployment and admin guidance
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* docs(faq): fix remaining recreate guidance
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
* docs(faq): clarify bulk install paths
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
---------
Signed-off-by: FenjuFu <fufenjupku@gmail.com>
Signed-off-by: FenjuFu <92919259+FenjuFu@users.noreply.github.com>
Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Co-authored-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
Also add Python CodeQL coverage in the security workflow so repository-level script regression checks stay green when Python source exists.
Signed-off-by: dongmucat <1127093059@qq.com>
Add more community-sourced questions (both Chinese and English):
- Troubleshooting CLI `skillhub publish` returning 400 (name conflict,
SKILL.md location/frontmatter, namespace membership, etc.)
- Required skill package structure (SKILL.md in root)
- "malformed input" on publish caused by non-UTF-8 / Chinese-path zips
- Per-package file-count limit and how to raise it
- Minimum server version for CLI features (v0.2.7+)
- PostgreSQL-only (no MySQL); plugins not distributable yet
- How to check server/CLI versions and customize via secondary dev
Signed-off-by: FenjuFu <fufenjupku@gmail.com>
Add questions frequently raised in the user community that were not yet
covered in the SkillHub FAQ, for both Chinese and English pages:
- Recommended deployment via the one-line script vs manual image pulls
- Redirected back to login page after deploying (manual deployment)
- Changing the admin password / why env changes need a restart
- Password change/reset requires email code (SMTP setup)
- Skill naming (English only; Chinese names error in OpenClaw)
- Whether unreviewed skills can be downloaded
- Hiding/removing GitHub & GitLab SSO login options
- Built-in Skill Scanner: iFLYTEK integration over Cisco's scanner (Apache-2.0)
- Which cisco-ai-skill-scanner version is used (unpinned in Dockerfile)
- Note that upgrades preserve registered skills; online docs link
Signed-off-by: FenjuFu <fufenjupku@gmail.com>
## Problem
Audit log timestamps displayed 8 hours later than actual time when JVM
default timezone != UTC. Root cause: `audit_log.created_at` was
`TIMESTAMP without time zone`, and `rs.getTimestamp()` interprets bare
values using JVM timezone.
## Solution
### Backend
- **V42 migration**: Upgrade `audit_log.created_at` from `TIMESTAMP` to
`TIMESTAMPTZ`, anchor historical data as UTC via `USING created_at AT
TIME ZONE 'UTC'` (same pattern as V18/V19/V23/V25/V36)
- **Read path**: `AdminAuditLogAppService.readInstant()` uses
`rs.getObject(col, OffsetDateTime.class).toInstant()`, result
independent of JVM timezone
- **Write path (filter params)**: `startTime`/`endTime` binding changed
from `Timestamp.from()` to `OffsetDateTime.ofInstant(instant,
ZoneOffset.UTC)` via `toUtcOffsetDateTime()` helper, symmetric with
read path
### Migration Safety
- `SET LOCAL lock_timeout = '30s'` (transaction-scoped, won't leak to pool)
- `DO $$ ... IF data_type = 'timestamp without time zone' THEN ... ELSE
... END $$` idempotent guard with dual-branch `RAISE NOTICE`
- Safe retry: re-running won't double-apply `AT TIME ZONE 'UTC'`
### Test Coverage (10 tests, 477 total suite)
- `rowMapper_readsCreatedAtAsInstant` — UTC offset regression
- `rowMapper_normalisesNonUtcOffsetToInstant` — Non-UTC offset (+08:00)
- `rowMapper_returnsNullTimestampWhenColumnIsNull` — Null path
- `rowMapper_isIndependentOfJvmDefaultTimezone` — JVM TZ=Asia/Shanghai
drift prevention with `verify(rs, never()).getTimestamp()`
- `@ParameterizedTest buildWhereClause_bindsTimeRangeAsOffsetDateTime` —
3 cases (both/startOnly/endOnly) for filter param binding
- `@BeforeEach setUp()` — Mock isolation to prevent cross-test stub
accumulation
## Quality Gates
- [x] `make test-backend-app` passes (477 tests, 0 failures)
- [x] No Controller changes, `make generate-api` not needed
- [x] No frontend changes, typecheck/lint/e2e not needed
## Deployment
V42 must run before new code (guaranteed by Spring Boot startup sequence
→ Flyway executes before app accepts traffic). Rolling deployment:
- New pod + migrated column: correct
- Old pod + migrated column: old code reads TIMESTAMPTZ correctly (pgjdbc
returns absolute instant)
## Related Docs
- `docs/15-backend-time-governance-plan.md` §3.1: V42 progress registered
- `docs/16-backend-time-inventory.md` §3.1: V42 listed
- Same migration pattern: V18/V19/V23/V25/V36
Resolve historical naming drift between protocol spec and CLI by adopting the
plural form across both docs:
- docs/07-skill-protocol.md: drop the drift caveat; the four-tier priority is
now stated as .agents/skills / ~/.agents/skills / .claude/skills /
~/.claude/skills directly.
- docs/00-product-direction.md: align with the same plural form.
The CLI already uses .agents/skills (cli/src/agents/profiles/generic-fallback.ts
and cli/src/agents/resolver.ts). No code change required.
- Distinguish user vs project install scope via explicit --scope flag
- Interactive mode prompts for scope when --scope/--agent/--dir not provided
- Non-interactive bare install preserves existing behavior (backward compatible)
- Mutual exclusion: --dir cannot be combined with --scope or --agent
- Symmetric fallback: --scope user falls back to ~/.agents/skills,
--scope project falls back to <cwd>/.agents/skills
- Strict TTY check requires both stdin and stdout TTY plus no --json
- Scope-aware candidate generation avoids root.startsWith(cwd) misjudgement
when cwd === home or paths overlap
- Correct gemini-cli (.gemini/skills) and kiro-cli (.kiro/skills) paths
in install path tables across README and guide docs
- Note CLI fallback uses .agents/skills (with s) in skill protocol doc
- Add publish script with env validation, git checks, and build/test/pack preflights
- Add comprehensive test suite for publish workflow (302 lines)
- Update cli/package.json with @astron-team scope and full npm metadata
- Add README.md with user-focused documentation and registry info
- Add .env.example template for publish configuration
- Add Apache 2.0 LICENSE
- Add Makefile targets for build/test/lint/typecheck/publish workflows
- All publish targets include .env.local validation
- Update installation instructions across all documentation to use @astron-team/skillhub
Document the full CLI workflow so users can understand configuration precedence, install targets, local state files, troubleshooting, and local verification steps.
Revert emailVerified check in EmailDomainAccessPolicy to preserve
backward compatibility with GitHub/GitLab OAuth users. Instead, null
unverified emails in CustomOidcUserService.toOAuthClaims() so
EmailDomainAccessPolicy naturally denies them via null email.
Add SLF4J logging to CustomOidcUserService for OIDC authentication
flow tracing and failure diagnostics.
Add registration ID collision warning to deployment docs.