Guide for contributors working on the ogiri codebase.
- Java 17+
- Kotlin 2.0.x
- Gradle 8.x
- PostgreSQL (for running samples)
| Command | Description |
|---|---|
./gradlew build |
Compile all modules and run tests |
./gradlew test |
Run test suite only |
./gradlew :ogiri-core:test |
Run core library tests only |
./gradlew clean |
Remove build artifacts |
./gradlew spotlessApply |
Auto-format code |
./gradlew spotlessCheck |
Verify formatting |
./gradlew :sample:sample-kotlin:bootRun # Kotlin sample
./gradlew :sample:sample-java:bootRun # Java sampleRequires PostgreSQL on localhost:5432. See sample/README.md for setup.
ogiri/
├── ogiri-core/ # Core library: interfaces, filter, token service
│ ├── src/main/kotlin/com/quantipixels/ogiri/security/
│ │ ├── core/ # AuthHeader, JsonCodec, exceptions
│ │ ├── tokens/ # OgiriTokenService, OgiriTokenRepository, OgiriToken
│ │ ├── web/ # OgiriTokenAuthenticationFilter
│ │ ├── spi/ # OgiriUserDirectory, OgiriAuditHook, OgiriRateLimitHook
│ │ ├── helpers/ # AuthenticationBypassDecider, SecurityHelpers
│ │ ├── routes/ # OgiriRouteRegistry, OgiriRoute
│ │ └── config/ # OgiriSecurityAutoConfiguration
│ ├── src/test/kotlin/ # JUnit 5 tests
│ └── src/main/resources/ogiri/db/ # Bundled SQL schemas (PostgreSQL, MySQL, H2)
├── ogiri-jpa/ # JPA adapter: OgiriBaseTokenEntity, JPA auto-configuration
├── ogiri-jdbc/ # JDBC adapter: OgiriBaseTokenRow, OgiriJdbcTokenRepository
├── ogiri-caffeine/ # Caffeine token lookup cache module
├── ogiri-redis/ # Redis token lookup cache module
├── sample/
│ ├── sample-java/ # Pure Java example (port 48080)
│ ├── sample-kotlin/ # Kotlin example (port 48081)
│ └── sample-react/ # React + TypeScript example (port 5173)
├── docs/ # Documentation (MkDocs)
└── .github/workflows/ # CI/CD pipelines
- Indentation: 2 spaces
- Nullability: Explicit with
?; avoid!!outside tests - Naming: PascalCase for classes, camelCase for functions
- Tests: Backticked names:
`should rotate token outside batch window` - Formatting: Run
spotlessApplybefore committing
./gradlew test # All tests
./gradlew :ogiri-core:test # Core onlyCoverage report: ogiri-core/build/reports/jacoco/test/html/index.html
- Use JUnit 5 with Spring test utilities
- Place tests in
src/test/kotlin/<package>/<Name>Test.kt - Use in-memory fakes (e.g.,
InMemoryTokenRepository) - When modifying token logic, add
AuthHeaderserialization tests - When changing schemas, update persistence tests
| Component | Coverage |
|---|---|
| AuthenticationBypassDecider | 100% |
| AuthHeader | 90% |
| OgiriTokenAuthenticationFilter | 70% |
| OgiriTokenService (sub-tokens) | 25% |
| OgiriSecurityAutoConfiguration | 0% |
Install hooks for code quality enforcement:
lefthook install- Pre-commit: Runs
spotlessCheck - Pre-push: Runs full build
Use Conventional Commits:
feat: add chat sub-token renewal
fix: adjust expiry parsing
refactor: extract validation logic
docs: update configuration guide
test: add rotation edge cases
chore: bump version to 1.0.2
- Create feature branch from
main - Make changes with tests
- Run
./gradlew build spotlessCheck - Push and create PR
- Link related issues
- Wait for CI and review
Version is defined in settings.gradle.kts:
val projectVersion = System.getenv("RELEASE_VERSION") ?: "3.0.1"RELEASE_VERSION=1.0.2 ./gradlew build./gradlew bumpVersion -PnewVersion=1.0.2Push a git tag to trigger the release workflow:
# 1. Update version in .ogiri-version
# 2. Update CHANGELOG.md
# 3. Commit changes
RELEASE_VERSION="$(tr -d '[:space:]' < .ogiri-version)"
git add .ogiri-version CHANGELOG.md
git commit -m "chore: bump version to ${RELEASE_VERSION}"
# 4. Create and push tag
git tag "v${RELEASE_VERSION}"
git push origin ori "v${RELEASE_VERSION}"GitHub Actions will:
- Build and test
- Sign artifacts with GPG
- Publish to Maven Central
- Create GitHub release
| Workflow | Trigger | Purpose |
|---|---|---|
build.yml |
All pushes | Compile modules |
test.yml |
All pushes | Run tests with coverage |
lint.yml |
All pushes | Verify formatting |
release.yml |
Tag v*.*.* |
Publish to Maven Central |
snapshot.yml |
Push to ori |
Deploy snapshots |
Configure in GitHub repository settings:
| Secret | Purpose |
|---|---|
OSSRH_USERNAME |
Central Portal token username |
OSSRH_PASSWORD |
Central Portal token password |
GPG_PASSPHRASE |
GPG passphrase |
GPG_PRIVATE_KEY |
ASCII-armored GPG private key |
Export GPG key:
export KEY_ID=your_gpg_key_id
gpg --armor --export-secret-keys "$KEY_ID"export RELEASE_VERSION="$(tr -d '[:space:]' < .ogiri-version)"
export OSSRH_USERNAME=your_portal_token_username
export OSSRH_PASSWORD=your_portal_token_password
export KEY_ID=your_gpg_key_id
export GPG_PRIVATE_KEY="$(gpg --armor --export-secret-keys "$KEY_ID")"
export GPG_PASSPHRASE=your_gpg_passphrase
./gradlew clean check publish
authorization="$(printf '%s:%s' "$OSSRH_USERNAME" "$OSSRH_PASSWORD" | base64 | tr -d '\n')"
curl \
--fail \
--request POST \
--header "Authorization: Bearer $authorization" \
"https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/com.quantipixels?publishing_type=automatic"- Tests pass:
./gradlew test - Formatting verified:
./gradlew spotlessCheck -
CHANGELOG.mdupdated - Version updated in
.ogiri-version - Tag created and pushed
- CI workflow completed
- Implement
OgiriSubTokenRegistrationbean - Define
name,clientIdFor(),expiry(),includeByDefault - Add tests in
TokenServiceSubTokenTest - Document in
docs/sub-tokens.md
- Update
OgiriTokenService.rotateTokensIfNeeded() - Add tests in
OgiriTokenAuthenticationFilterTest - Update
docs/configuration.md
- Create class extending
OgiriBaseToken - Implement
OgiriTokenRepository<MyToken> - Provide custom
OgiriTokenService<MyToken>bean - Set
ogiri.security.register-filter=false
- Never log raw tokens
- Use
SecurityServiceExceptionfor auth errors - Use
IdentifierPolicyfor validation - Register public routes to prevent lockouts