Architecture¶
CodeScoring.Save uses a microservice architecture. Backend API and authentication / authorization are split into separate services: Save API service and cs-auth (Auth/RBAC service).
Technology Stack¶
- Development language: Go 1.25+.
- Metadata storage: PostgreSQL 14+ (recommended for production) or SQLite 3.x.
- Object storage: S3-compatible storage (MinIO, AWS S3, Yandex Object Storage) or local file system.
- Authentication: RSA-2048 JWT with JWKS, issued by cs-auth and validated locally by Save through the public key.
Architecture Diagram¶
flowchart TB
UI["Web UI · React"]
PM["Package managers<br/>npm · mvn · docker · nuget · pypi · go"]
LB["Load Balancer / Ingress"]
SAVE["<b>Save API service</b><br/>stateless · pods 1..N"]
AUTH["<b>cs-auth</b> (Auth / RBAC)<br/>stateless · pods 1..N<br/>shared RSA key"]
DB[("<b>Metadata DB</b><br/>see note below")]
OBJ[("<b>Object Storage</b><br/>S3-compatible / local FS")]
UI --> LB
PM --> LB
LB --> SAVE
LB -- "/v2/token (Docker auth flow)" --> AUTH
SAVE -- "action authorization · JWKS · audit events" --> AUTH
SAVE --> DB
SAVE --> OBJ
AUTH --> DB
Database schema
In a PostgreSQL deployment, both services can share one database instance and one schema; data is separated by table name prefixes. In a SQLite deployment, each service uses a separate database file.
System Components¶
Save API service¶
The main backend service that processes incoming requests to repositories and the administrative API.
Functions:
- Processing HTTP/HTTPS requests from package managers and the web interface.
- Token validation and integration with the dedicated Auth/RBAC service.
- Routing requests to the storage backend.
- Integration with CodeScoring.OSA through OSA Proxy for artifact scanning when downloads are routed through the proxy.
- Applying security policies and cleanup policies.
- Structured logging and Prometheus metrics export.
Technical characteristics:
- Stateless architecture for horizontal scaling.
- Graceful shutdown during updates.
- Health checks for Kubernetes.
Auth/RBAC service¶
A dedicated authentication and authorization service deployed separately from Save API service.
Functions:
- Authentication of users and service accounts.
- Issuing and refreshing JWT tokens (RSA-2048, alg=RS256).
- Managing roles and permissions at global / project / repository levels.
- Managing API keys, robot accounts, and project membership.
- Audit of access-related and system-wide operations.
- Internal API for interaction with Save API service.
- OCI Distribution Spec token endpoint for Docker / Helm / oras clients.
Technical characteristics:
- Separate codebase and separate entry point.
- Dedicated PostgreSQL schema in a shared database instance, or a separate SQLite file.
- Horizontal scaling independently from Save API service.
- Required scaling condition: all replicas must share the same RSA key for signing JWT tokens.
Storage Backend¶
Artifact storage subsystem:
- Metadata Store — PostgreSQL or SQLite for metadata.
- Blob Store — object storage for artifacts: S3-compatible storage or file system.
Operations: storing and retrieving artifacts, version management, checksum generation.
Background Workers¶
Background processes for deferred tasks:
- Cleanup policies — scheduled removal of outdated artifacts.
- Background metadata operations.
- Asynchronous delivery of audit events to cs-auth.
Web UI¶
Web interface for managing projects, repositories, artifacts, and access settings. Module capabilities are described on the Functional characteristics page.
Service Interaction¶
- External clients (Web UI and package managers) access only Save API service through ingress; cs-auth is not exposed outside the cluster.
- Save API service delegates authentication, role checks, and token issuing to Auth/RBAC service.
- Both services can use separate or shared PostgreSQL instances, but they work in separate schemas/databases.
- Save API service works directly with metadata store and object storage; cs-auth works only with metadata store and has no access to object storage.
- Save calls cs-auth in several cases:
- Proxying part of the administrative API — Save forwards requests without processing them itself.
- Checking credentials that are not present in the local pod cache — Basic Auth, API key, NuGet API key, NPM token, Docker token.
- Sending audit events — one event log for all services.
- All internal endpoints are additionally protected by a shared secret.
- On the hot path, Save validates Bearer JWT locally using a cached JWKS public RSA key without a network call to cs-auth.
Data Model¶
Main Entities¶
Project¶
Repository¶
- ID
- Project ID
- Name
- Type (proxy/hosted)
- Format (maven, npm, docker, nuget, pypi, go, raw)
- Configuration
- Storage Backend
- Cleanup Policies
- Created/Updated timestamps
Artifact¶
- ID
- Repository ID
- Group/Namespace
- Name
- Version
- Format-specific metadata
- Size
- Checksums (MD5, SHA1, SHA256)
- Upload date
- Last accessed
User¶
- ID
- Username
- DisplayName
- Email
- PasswordHash (bcrypt)
- IsActive
- AuthSource (local / ldap / oidc)
- LastLoginAt
- Created/Updated timestamps
Role / Robot account¶
- ID
- Name
- Description
- Permissions (global / project / repository scopes)
- IsAdmin
- IsSystem
- API keys (only for robot accounts)
- Created/Updated timestamps
APIKey¶
- ID
- UserID (FK to user with IsService=true)
- KeyPrefix (first 12 hex characters of the plaintext key, used for O(1) lookup)
- KeyHash (bcrypt(plaintext))
- Description
- ExpiresAt (NULL = no expiration)
- LastUsedAt
- CreatedAt
The full plaintext key is returned to the client only once, when it is created.
Cleanup Policy¶
Installation Options¶
CodeScoring.Save supports several installation profiles. Requirements, profile differences, and deployment steps are described in Installation Options.
Security¶
Authentication and Authorization¶
- JWT tokens for API, issued by Auth/RBAC service
- Basic Auth / API keys for package managers and service accounts
- Role-based access control (RBAC) with three scopes (global / project / repository), managed by the dedicated Auth/RBAC service
- Inter-service token validation through the JWKS endpoint
Client-side authentication methods are described in detail in Authentication.
Data Encryption¶
Save and cs-auth services do not perform application-level encryption; encryption is provided by the underlying components:
- Encryption at rest is provided by the selected object storage (S3 server-side encryption, MinIO encryption-at-rest) and PostgreSQL (through TDE extensions or volume-level encryption).
- Encryption in transit is provided by TLS on ingress / load balancer and in inter-service connections.
- User passwords and robot account API keys are stored as bcrypt hashes in cs-auth.
Audit¶
- Complete logging of all operations into a unified event log used by both services.
- Export to machine-readable format (JSON) and HTML. A single export can contain up to 10,000 records.
- Supported filters for list/export:
from,to,username,action,resource_type,q,user_id. In the web UI, search is available only by username, resource type, and action.
Performance¶
Optimizations¶
- Caching:
- In-process TTL cache
credential -> PermissionSetin each Save API service pod, removing the round trip to cs-auth for repeated requests with the same credentials. -
Local Bearer JWT validation through the JWKS public key, without a network call to cs-auth on the hot path. JWKS is refreshed periodically in the background.
-
Connection pooling:
- Database connection pool.
-
Reused HTTP clients for upstream repositories.
-
Parallel processing:
- Concurrent uploads / downloads through goroutines.
-
Batch (bulk) operations API.
-
Streaming large files:
- Artifacts are transferred through
MultiWriterwithout buffering the full file in memory. - Atomic disk writes through temp-file rename to avoid corruption during concurrent writes.
Target Metrics¶
For a basic installation:
- Latency (p95): < 100 ms for cached requests
- Throughput: 300+ requests/sec
- Concurrent uploads: 100+
- Max artifact size: 5 GB
Scaling¶
Horizontal Scaling¶
- Save API service: stateless, scaled by adding replicas.
- Auth/RBAC service: stateless, scaled by adding replicas. Required condition: all replicas must share the same RSA key for signing JWT tokens (
AUTH_JWT_RSA_KEY_PATH). - PostgreSQL: vertical scaling + read replicas if needed.
- Object storage: scaled horizontally by the provider (S3, MinIO in a cluster).
Vertical Scaling¶
- Increasing pod resources.
- Increasing PostgreSQL capacity.
- Expanding object storage.
Monitoring and Observability¶
Metrics (Prometheus)¶
Save exports metrics in OpenTelemetry format with Prometheus exporter.
HTTP metrics:
http_requests_total{method,path,status}— HTTP request counter.http_request_duration_seconds{method,path,status}— latency histogram.http_requests_active— active request gauge.
Operation metrics:
storage_operations_total{operation,backend,success}— storage operations.artifact_uploads_total{artifact_type,repository}— artifact uploads.artifact_downloads_total{artifact_type,repository}— artifact downloads.proxy_requests_total{artifact_type,repository,cache_hit,success}— proxy requests. Cache hit rate is calculated asrate(proxy_requests_total{cache_hit="true"}[5m]) / rate(proxy_requests_total[5m]).cleanup_operations_total{policy_type,repository,artifacts_deleted,success}— cleanup operations.
Logging¶
- Structured logs in JSON format (or text, depending on configuration).
- Levels:
debug,info,warn,error. - Output: stdout, stderr, or file.
- Compatible with any log aggregator that can process JSON (ELK, Loki, Vector).
Availability and Resilience¶
CodeScoring.Save does not implement automatic failover, point-in-time recovery, or built-in backup on its own. These capabilities are provided by the standard tools of the infrastructure where the service is deployed:
- Multiple Save and cs-auth replicas in Kubernetes behind a load balancer provide availability if one pod fails.
- PostgreSQL replication and database backup are configured through the PostgreSQL operator or managed service.
- Object storage snapshots and replication are handled by the S3-compatible storage provider.
- Multi-node Kubernetes cluster provides resilience to node failure.
Save does not lose data on restart because all state is external: metadata is stored in the database and artifacts are stored in object storage. Stateless Save architecture allows scaling and pod recreation without data loss.