Kagami is a mirror server for Maven repositories. This document describes the API endpoints available for retrieving and managing artifacts, and the server-rendered web UI.
Artifact operations are available at /artifacts. The web UI (home, repository browse and token generation pages) is server-rendered from /, /browse/** and /token.
The web UI requires authentication through either form-based login or OIDC/OAuth2:
- Username:
spring.security.user.name(default:demo) - Password:
spring.security.user.password(default:{noop}demo)
- Configured via OAuth2 client settings
- Access restricted by email patterns in
kagami.authentication.allowed-name-patterns
The web interface features:
- Styled login and logout pages matching the application design
- Unified header navigation across all pages showing logged-in username
- Easy access to logout functionality and token generation from the header
- Server-rendered pages with Mustache templates and htmx partials
- Public repositories: No authentication required (GET and HEAD)
- Private repositories: JWT-based authentication. The JWT can be sent either as a
Bearer token (
Authorization: Bearer <jwt>) or as the password part of a Basic auth header (Authorization: Basic base64(<username>:<jwt>)). The username is ignored; the password is resolved as the bearer token. The Basic form exists so Maven/Gradle clients configured with the standard<username>/<password>server settings work out of the box. - GET requires the
artifacts:readscope, DELETE requires theartifacts:deletescope, and the cache garbage collection endpoints requireartifacts:admin(authenticated web UI sessions with USER role are also accepted).
JWT tokens must be generated through the authenticated web UI. See the Token Management section for details.
The following endpoints are accessible without authentication:
/login- Login page/logout- Logout page/css/**,/js/**,/fonts/**- Static assets/favicon.svg- Favicon/error- Error pages/actuator/**- Spring Actuator endpoints (health, metrics, etc.)/.well-known/**- Well-known endpoints/openid/v1/jwks- JWKS endpoint for JWT verification- Public repository artifacts (
/artifacts/{publicRepoId}/**)
- Requests:
application/x-www-form-urlencodedfor form endpoints - Responses:
text/plainfor the token endpoint, various MIME types for artifact downloads
Download an artifact from the repository. This endpoint serves the actual file content. If the artifact is not in local storage yet, it is fetched from the remote repository on demand. HEAD is also supported.
Parameters:
repositoryId(path, required): Repository identifierartifactPath(path, required): Full path to the artifact within the repository
Example Request:
GET /artifacts/central/org/springframework/spring-core/5.3.21/spring-core-5.3.21.jar
Response:
- Binary content of the requested file
- Appropriate
Content-Typeheader based on file extension Content-Lengthheader with file sizeCache-Controlheader:max-age=31536000, publicfor public repositories,max-age=31536000, privatefor private repositoriesContent-Disposition: inlinefor XML and text files, including.properties; other files useattachment. The file name is included in the header.
Status Codes:
200 OK: File found and returned401 Unauthorized: Authentication required (for private repository)403 Forbidden: Token lacks required scope404 Not Found: Repository, file, or remote artifact not found500 Internal Server Error: Server error
Delete an artifact or directory from the repository.
Parameters:
repositoryId(path, required): Repository identifierartifactPath(path, required): Full path to the artifact or directory within the repository
Example Request:
DELETE /artifacts/central/org/springframework/spring-core/5.3.21/spring-core-5.3.21.jar
Response:
204 No Content: Successful deletion. When the request comes from the web UI (htmx), the response carries anHX-Trigger: refreshEntriesheader so the directory listing refreshes itself.401 Unauthorized: Authentication required (for private repository or if not logged in)403 Forbidden: Token lacks required scope404 Not Found: Repository or file not found500 Internal Server Error: Server error
The garbage collection endpoints remove cache-bookkeeping-only directories from one repository. A candidate directory must contain exactly one of these file sets:
maven-metadata.xmlandmaven-metadata.xml.sha1resolver-status.propertiesonly
Every file in the candidate must be older than olderThan. The default is one hour.
Directories with any other file or subdirectory are left unchanged.
These endpoints require the artifacts:admin authority, including for public
repositories. The default editors group does not include this authority. The gc
path is reserved for this maintenance operation. Bearer-authenticated API calls do not
need a CSRF token; browser-session POST requests do.
Preview eligible directories without changing storage.
Parameters:
repositoryId(path, required): Repository identifierolderThan(query, optional): ISO-8601 duration; defaults toPT1H
Example Request:
GET /artifacts/central/gc?olderThan=PT1H
Authorization: Bearer <jwt>
Response:
[
{
"path": "org/example/missing",
"lastModified": "2026-09-24T10:00:00Z"
}
]Collect eligible directories. The operation rechecks each directory before deleting it and removes only its eligible bookkeeping files, so a concurrent artifact download is preserved.
Parameters:
repositoryId(path, required): Repository identifierolderThan(query, optional): ISO-8601 duration; defaults toPT1H
Example Request:
POST /artifacts/central/gc?olderThan=PT1H
Authorization: Bearer <jwt>
Response:
{
"collectedPaths": ["org/example/missing"],
"failures": []
}Status Codes:
200 OK: Preview or collection completed400 Bad Request:olderThanis negative or invalid401 Unauthorized: Authentication required403 Forbidden: Principal lacksartifacts:admin404 Not Found: Repository is not configured500 Internal Server Error: Storage error while listing or deleting
Generate a JWT token for accessing private repositories. This endpoint requires USER role authentication.
Authentication Required: Yes - Users must be logged in to the web UI with USER role to generate tokens. JWT tokens cannot be used to generate new tokens.
Web Interface: The recommended approach is to use the web-based token generation interface at /token which provides:
- Repository selection with checkboxes
- Permission scope selection (artifacts:read, artifacts:delete, artifacts:admin)
- Human-friendly expiration time input (hours, days, months) with 6-month default
- Warning for long-duration tokens (>6 months)
- Copy functionality for generated tokens
- Build tool configuration examples (Maven, Gradle Groovy, Gradle Kotlin) with copy buttons
Parameters (form-urlencoded):
expires_in(optional): Token expiration time in hours (default: 3)repositories(optional): Comma-separated list of repository IDs to accessscope(optional): Comma-separated list of scopes (artifacts:read,artifacts:delete,artifacts:admin)
Example Request:
POST /token
Content-Type: application/x-www-form-urlencoded
Cookie: JSESSION=<jsession-id>
repositories=spring-enterprise,gemfire&scope=artifacts:read,artifacts:delete&expires_in=24
Response:
eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvIiwiYXVkIjoia2FnYW1pIiwiaWF0IjoxNzIzMDE2...
The generated JWT token includes:
sub(subject): The authenticated usernameiss(issuer): The server URLaud(audience): "kagami"iat(issued at): Token creation timestampexp(expiration): Token expiration timestampscope: Granted permissionsrepositories: Accessible repository IDs
Status Codes:
200 OK: Token generated successfully401 Unauthorized: Authentication required403 Forbidden: USER role required
Important Notes:
- This endpoint requires USER role authentication (not just any authentication)
- JWT tokens cannot be used to generate new JWT tokens - only session-based authentication is accepted
- It is recommended to use the web UI at
/tokento generate tokens rather than calling this API directly
API endpoints return appropriate HTTP status codes. For client errors (4xx) and server errors (5xx), the response body may be empty or contain error details.
For authentication errors (401) on /artifacts/**, the response includes a WWW-Authenticate header:
WWW-Authenticate: Bearer- for missing authenticationWWW-Authenticate: Bearer error="invalid_token", error_description="..."- for invalid tokensWWW-Authenticate: Bearer error="insufficient_scope", error_description="..."- for insufficient permissionsWWW-Authenticate: Basic realm="Kagami"- additionally added for non-browser clients (browsers are excluded to avoid a native credential dialog)
The generated JWT scope and repositories claims are serialized as JSON arrays.