Development Guide
Guide for contributors working on the ogiri codebase.
Prerequisites
- Java 17+
- Kotlin 2.0.x
- Gradle 8.x
- PostgreSQL (for running samples)
Build Commands
| 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 |
Running Samples
./gradlew :sample:sample-kotlin:bootRun # Kotlin sample
./gradlew :sample:sample-java:bootRun # Java sample
Requires PostgreSQL on localhost:5432. See sample/README.md for setup.
Project Structure
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
Code Style
- 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
Testing
Running Tests
./gradlew test # All tests
./gradlew :ogiri-core:test # Core only
Coverage report: ogiri-core/build/reports/jacoco/test/html/index.html
Test Guidelines
- 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
Current Coverage
| Component | Coverage |
|---|---|
| AuthenticationBypassDecider | 100% |
| AuthHeader | 90% |
| OgiriTokenAuthenticationFilter | 70% |
| OgiriTokenService (sub-tokens) | 25% |
| OgiriSecurityAutoConfiguration | 0% |
Git Hooks
Install hooks for code quality enforcement:
./gradlew setupDev
- Pre-commit: Runs
spotlessCheck - Pre-push: Runs full build
Commit Guidelines
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
Pull Request Process
- 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 Management
Version is defined in settings.gradle.kts:
val projectVersion = System.getenv("RELEASE_VERSION") ?: "1.0.1"
Override Version
RELEASE_VERSION=1.0.2 ./gradlew build
Bump Version
./gradlew bumpVersion -PnewVersion=1.0.2
Release Process
Automated Release (Recommended)
Push a git tag to trigger the release workflow:
# 1. Update version in settings.gradle.kts
# 2. Update changelog.md
# 3. Commit changes
git add settings.gradle.kts docs/changelog.md
git commit -m "chore: bump version to 1.0.2"
# 4. Create and push tag
git tag v1.0.2
git push origin main v1.0.2
GitHub Actions will:
- Build and test
- Sign artifacts with GPG
- Publish to Maven Central
- Create GitHub release
CI/CD Workflows
| 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 main | Deploy snapshots |
Required Secrets
Configure in GitHub repository settings:
| Secret | Purpose |
|---|---|
OSSRH_USERNAME | Sonatype username |
OSSRH_PASSWORD | Sonatype password |
GPG_KEY_ID | GPG key ID |
GPG_PASSPHRASE | GPG passphrase |
GPG_PRIVATE_KEY | Base64-encoded GPG key |
NPM_TOKEN | npm registry auth token for ogiri-security-client |
Export GPG key:
gpg --export-secret-key <KEY_ID> | base64
Manual Release (Not Recommended)
export OSSRH_USERNAME=your_username
export OSSRH_PASSWORD=your_password
./gradlew test
./gradlew publish -Psigning.gnupg.executable=gpg
Release Checklist
- [ ] Tests pass:
./gradlew test - [ ] Formatting verified:
./gradlew spotlessCheck - [ ] changelog.md updated
- [ ] Version updated in
settings.gradle.kts - [ ] Tag created and pushed
- [ ] CI workflow completed
- [ ] npm publish:
NPM_TOKENsecret configured (for client releases)
Common Tasks
Adding a Sub-Token Type
- Implement
OgiriSubTokenRegistrationbean - Define
name,clientIdFor(),expiry(),includeByDefault - Add tests in
TokenServiceSubTokenTest - Document in
docs/sub-tokens.md
Modifying Token Rotation
- Update
OgiriTokenService.rotateTokensIfNeeded() - Add tests in
OgiriTokenAuthenticationFilterTest - Update
docs/configuration.md
Extending Token Entity
- Create class extending
OgiriBaseToken - Implement
OgiriTokenRepository<MyToken> - Provide custom
OgiriTokenService<MyToken>bean - Set
ogiri.security.register-filter=false
Security
- Never log raw tokens
- Use
SecurityServiceExceptionfor auth errors - Use
IdentifierPolicyfor validation - Register public routes to prevent lockouts