| Generated code | osis-core/src/main/java/com/scality/osis/App.java is rewritten by the app Gradle task on every compileJava. Hand-edits to VERSION or DATE will be silently clobbered. Version bumps must change osisVersion in root build.gradle:3, not App.java. |
| Module dependency direction | Strict: osis-app → osis-core → storage-platform-clients. Flag any new dependency that reverses this (e.g. a controller importing a client directly, or osis-core referencing osis-app). |
| Spring patterns | @Autowired field injection vs constructor injection — prefer constructor injection for new beans. @Async methods must not call other @Async methods on the same bean (self-invocation bypasses the proxy). Bean scope changes are breaking. |
| Exception handling | No swallowed exceptions (catch (Exception e) {} with no log/rethrow). Vault/S3/STS SDK errors should be mapped to the appropriate OsisException subtype in model/exception/, not leaked raw. NoSuchEntity from AssumeRoleBackbeat has a specific meaning (Role does not exist → run SetupAssumeRole); don't collapse it into a generic 500. |
| Caching | The four caches (markerCache, assumeRoleCache, listAccountsCache, accountIDCache) are configured under osis.scality.vault.cache.*. Changes to TTLs, capacity, or cache keys need a reason — assumeRoleCache TTL is 50 minutes because the session token is valid for 60. Going above 60 will break flows. |
| Secret handling | Secret keys are encrypted via osis.security.keys.cipher (32-byte) and stored in Redis Sentinel hash osis:s3credentials keyed <Username>__<AccessKeyID>. Any change to the encryption path, key format, or hash schema is a data-migration concern. Never log secrets, access keys, or session tokens. |
| AWS SDK usage | Region must come from config (osis.scality.region), not hardcoded. Credentials providers must use the assumeRoleCache for user-scoped calls, never super-admin creds for user-facing flows. STS session token refresh must use AWS SDK refresh() — don't roll your own. |
| Dependency pinning | vaultclientVersion, springBootVersion, AWS SDK versions live in root build.gradle. Bumping any of these requires a coverage check and a Sonatype publish step; flag if the bump is unexplained. |
application.properties | The in-tree file is a template. Real values mount at /conf/application.properties in the container. Flag any commit that bakes in a non-default endpoint, real access key, or production hostname into the template. |
| Test coverage | JaCoCo enforces 75% minimum via jacocoTestCoverageVerification. New service/controller code without a corresponding test will likely drop coverage and fail check. Exclusion list in jacoco.gradle:60-67 — don't expand it without a strong reason. |
| Logging | Use SLF4J Logger logger = LoggerFactory.getLogger(...). No System.out.println, no printStackTrace() in production code. Match log level to severity; DEBUG is fine, ERROR should mean operator-actionable. |
| Health endpoints | Health is served at /_/healthcheck, not the Spring default. New health indicators belong in osis-app/.../healthcheck/ and must extend HealthIndicator. Utapi healthcheck was intentionally removed in 2.2.5 — flag any attempt to re-add it without justification. |
| Vendored crypto | security/crypto/Hkdf* is vendored from an external library and explicitly excluded from coverage and lint. Don't refactor it as if it were ours; flag any modification. |
| OSE REST contract | OSE drives this service. Changes to request/response models in osis-core/.../model/ (e.g. OsisTenant, OsisUser, OsisS3Credential, Page*) are potentially breaking for OSE consumers. Flag field renames, removals, or required-vs-optional flips. |
| Security | OWASP-relevant: hard-coded credentials, SQL/log injection (no string-concatenated SQL or log args), path traversal in any file-read of crypto.yml or admin creds, secrets logged in error paths. |