# AGENTS.md
Guidance for AI coding agents working in the Apereo CAS source tree.
> This repository is for CAS contributors. If the task is deployment/configuration, prefer the WAR overlay approach instead of editing this repo.
## Big picture
- CAS is a very large Gradle monorepo. `settings.gradle` shows the main layering: `api/` defines contracts and config models, `core/` implements platform behavior, `support/` adds protocols/backends/features, and `webapp/` assembles runnable apps.
- The servlet app starts in `webapp/cas-server-webapp-init/src/main/java/org/apereo/cas/web/CasWebApplication.java`; startup is extensible through `ApplicationUtils.getApplicationEntrypointInitializers()`.
- Feature wiring is annotation-driven. Example: `core/cas-server-core-authentication/.../CasCoreAuthenticationAutoConfiguration.java` imports authentication sub-configurations behind `@ConditionalOnFeatureEnabled`.
- Support modules usually follow a family split such as `*-core`, storage variants (`*-jdbc`, `*-mongo`, `*-redis`), protocol/webflow modules, and a thin webapp assembly. OIDC, SAML, tickets, services, and MFA all follow this pattern in `settings.gradle`.
- `api/cas-server-core-api-configuration-model/.../CasConfigurationProperties.java` is the root of the `cas.*` config tree. Property classes are not passive POJOs: `ConfigurationMetadataGenerator` fails if a config model class is missing `@RequiresModule`.
## Conventions you should match
- Java 25 is required (`gradle.properties`); many sources use `import module java.base;`, Lombok `val`, and package-level `@NullMarked` via `package-info.java`.
- Spring config classes generally use `@AutoConfiguration` or `@Configuration(proxyBeanMethods = false)`, `@EnableConfigurationProperties(CasConfigurationProperties.class)`, `@ConditionalOnFeatureEnabled`, and bean methods with `@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)` plus `@ConditionalOnMissingBean`. See `support/cas-server-support-token-core/.../TokenCoreConfiguration.java`.
- Configuration model classes usually live under `api/.../configuration/model/**`, use Lombok accessors, and carry `@RequiresModule(name = "...")`; example: `LdapAuthorizationProperties`.
- Tests are organized by JUnit tags, not by the plain Gradle `test` task. The shared `buildSrc` test conventions disable `test` and generate tasks like `testAuthentication`, `testTickets`, etc. from `@Tag(...)` values found in `*Tests.java`.
- Related test scenarios are often grouped with `@Nested`; example: `support/cas-server-support-token-core/.../JwtBuilderTests.java`.
- Unalias Linux/macOS commands before you run them, specially `tree`, `find`, `grep`, `cat`, etc.
## Workflows that matter here
- List supported test buckets:
```bash
./gradlew -q testCategories
```
- Run repository test categories through the project script, not `gradle test`:
```bash
./testcas.sh --category authentication
./testcas.sh --category tickets --with-coverage
./testcas.sh --category oidc --debug
```
- Run one module or one class directly when narrowing a change:
```bash
./gradlew :core:cas-server-core-authentication:test --tests "*AuthenticationHandlerTests"
```
- Compile the tree without the expensive checks when you only need a fast validation pass:
```bash
./gradlew build --parallel -x test -x javadoc -x check
```
- Many `./testcas.sh` categories shell out to `ci/tests/**/run-*.sh` and require Docker on Linux; the script will refuse those categories when that prerequisite is missing.
## Practical boundaries
- Put new behavior in the narrowest module that already owns that concern; do not skip from `webapp/` straight into backend-specific code when an `api/` or `core/` seam already exists.
- When adding configuration, update the config model class first; otherwise metadata/docs generation will not understand the new property.
- For service-aware logic, look for `ServicesManager.findServiceBy(...)`; for ticket-aware logic, look for `ticketRegistry.getTicket(...)`. Those seams are used repeatedly across `core/` and `support/` and are usually the right integration points.
- Treat authentication, tickets, webflow, logout, MFA, and crypto as security-sensitive areas. Match existing CAS utilities and flows instead of introducing parallel mechanisms.
- Keep diffs surgical: this codebase already has strong patterns, so the fastest path is usually βcopy the nearest module family pattern and adapt itβ rather than inventing a new abstraction.
# CLAUDE.md
This repository is **Apereo CAS** (Central Authentication Service), a large-scale enterprise authentication server built with Java, Gradle, and Spring Boot. This file provides guidance for Claude Code and Claude AI assistants so that changes remain consistent with CAS project practices and reviewer expectations.
> **Note**: If you are trying to *deploy/configure* CAS, you should **not** be editing this repository directly; use the **WAR Overlay** approach instead. Building from source is for contributors only.
---
## Project Overview
- **Type**: Multi-module Gradle project (500+ modules)
- **Language**: Java 25+
- **Frameworks**: Spring Boot 4.x, Spring Cloud, Spring Webflow
- **Build Tool**: Gradle 9.x with parallel builds and configuration cache enabled
- **Architecture**: Modular design β API β Core β Support β Webapp layers
### Module Organization
```
api/ β Interface definitions and contracts
core/ β Core implementations of API contracts
support/ β Feature modules (LDAP, OIDC, SAML, Duo, etc.)
webapp/ β Web application modules (Tomcat, Jetty, etc.)
docs/ β User-facing documentation
ci/ β CI scripts and test helpers
style/ β Checkstyle, SpotBugs, ErrorProne configs
```
**Key principle**: `api/` modules define contracts, `core/` implements them, `support/` adds features. Always respect module boundaries and avoid circular dependencies.
---
## Goals for AI-Assisted Changes
When generating or modifying code in this project, optimize for:
- **Small, reviewable diffs** β keep changes focused and minimal
- **Consistency with existing patterns** β follow the conventions already in the codebase
- **Tests + docs** β every bug fix needs a test; user-facing changes need documentation
- **Build correctness** β respect Gradle module boundaries and dependency scopes
- **Security correctness** β authentication, authorization, and crypto must not be weakened
---
## Build & Run
### Full Build (Skip Tests)
```bash
./gradlew build --parallel -x test -x javadoc -x check
```
### Build Specific Module
```bash
./gradlew :core:cas-server-core-authentication:build
```
### Run Locally
```bash
cd webapp/cas-server-webapp-tomcat
../../gradlew bootRun
```
Access at: `https://localhost:8443/cas`
### Clean Build
```bash
./gradlew clean build --no-build-cache
```
---
## Testing
### Test Framework
Use `./testcas.sh` for comprehensive testing:
```bash
# See available test categories
./gradlew -q testCategories
# Run specific test category
./testcas.sh --category CategoryName
# Run specific test class
./testcas.sh --test TestClassName
# Run with coverage
./testcas.sh --category CategoryName --with-coverage
# Debug mode (port 5005)
./testcas.sh --category CategoryName --debug
```
### Run Single Test (Direct Gradle)
```bash
./gradlew :core:cas-server-core-authentication:test --tests "*AuthenticationHandlerTests"
./gradlew :support:cas-server-support-ldap:test --tests "*LdapAuthenticationHandlerTests.verifySuccess"
```
### Test Conventions
- Every bug fix needs a test
- New features require comprehensive test coverage
- Tests belong in the same module as the code they test
- Use `@SpringBootTest` for integration tests
- Use `@Nested` for organizing related test cases
---
## Code Conventions
### Java Style
- **Java version**: Java 25+ (use modern features: records, pattern matching, switch expressions, sealed classes)
- **Indentation**: 4 spaces, NO tabs
- **Braces**: Always use braces, even for single-line blocks
- **Null safety**: Use `@NullMarked` at package level (JSpecify annotations)
- **Conditionals**: Avoid needless `else` statements
- **Imports**: No unused imports (enforced by Checkstyle)
- **Line length**: 200 characters max
### Lombok Usage
Lombok is heavily used throughout the codebase:
- `@Getter` / `@Setter` for bean properties
- `@RequiredArgsConstructor` for dependency injection
- `@Slf4j` for logging (field name: `LOGGER`, static)
- `@ToString` / `@EqualsAndHashCode` with `doNotUseGetters = true`
- **Avoid** `@Data` (too implicit)
### Spring Configuration Patterns
- Use `@AutoConfiguration` or `@Configuration` for config classes
- Always use `@ConditionalOnFeatureEnabled` for feature toggles
- Use `@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)` for runtime-refreshable beans
- Use `@ConditionalOnMissingBean` to allow overrides
- Order beans with `@Order` or implement `Ordered`
### Bean Registration Example
```java
@Bean
@RefreshScope(proxyMode = ScopedProxyMode.DEFAULT)
@ConditionalOnMissingBean(name = "myService")
public MyService myService(final CasConfigurationProperties casProperties) {
return new DefaultMyService(casProperties);
}
```
### Package Structure
```java
@NullMarked
package org.apereo.cas.authentication;
import org.jspecify.annotations.NullMarked;
```
### Module Dependencies
- `api/` modules: Define interfaces only, minimal dependencies
- `core/` modules: Depend on corresponding `api/` modules
- `support/` modules: Can depend on `core/` and other `support/` modules
- Use `api` vs `implementation` dependency scopes appropriately
---
## Quality Checks
- **Checkstyle**: Enforced via `style/checkstyle-rules.xml` (line length: 200 chars)
- **SpotBugs**: Static analysis via `style/spotbugs-excludes.xml`
- **ErrorProne**: Enabled by default (skip with `-DskipErrorProneCompiler=true`)
- **NullAway**: Null safety analysis (skip with `-DskipNullAway=true`)
---
## Documentation
- Update `docs/cas-server-documentation/` for any user-facing change
- Public APIs require Javadoc with `@since` version tags
- Configuration properties need `@RequiresModule` annotation
---
## Security Guidance
**CRITICAL**: Do not blindly accept or generate changes in these areas without careful review and testing:
- Authentication flows (login, logout, SSO)
- Authorization and access control
- Ticket validation and issuance
- Cryptographic operations
- MFA workflows
- Session management
- Input validation and sanitization
### Security Rules
- Never weaken existing security constraints
- Always validate user input
- Use CAS-provided crypto utilities (don't roll your own)
- Test security changes with both positive and negative cases
- Follow the principle of least privilege
---
## What Claude Should NOT Do
- β Add dependencies without justification
- β Reformat unrelated code
- β Skip tests or quality checks
- β Replace CAS-specific patterns with generic alternatives
- β Make broad refactorings without discussion
- β Modify authentication/authorization without thorough testing
- β Generate code that weakens security posture
---
## PR Checklist
- β
Builds locally without errors
- β
Tests pass (or new tests added)
- β
Checkstyle/SpotBugs/ErrorProne clean
- β
Documentation updated for user-facing changes
- β
Scope is focused and reviewable
- β
No unused imports or trailing whitespace
- β
Security implications reviewed
---
## Useful Gradle Tasks
```bash
./gradlew tasks # List all tasks
./gradlew -q testCategories # Show test categories
./gradlew :core:cas-server-core-authentication:dependencies # Module dependencies
./gradlew dependencyUpdates # Check for updates
./gradlew javadoc # Generate Javadoc
```
---
## Additional Resources
- [Contributor Guidelines](https://apereo.github.io/cas/developer/Contributor-Guidelines.html)
- [Build Process](https://apereo.github.io/cas/developer/Build-Process.html)
- [Documentation](https://apereo.github.io/cas/development)
- [Architecture](https://apereo.github.io/cas/development/planning/Architecture.html)
## Behavioral guidelines
### 1. Think Before Coding
**Don't assume. Don't hide confusion. Surface tradeoffs.**
Before implementing:
- State your assumptions explicitly. If uncertain, ask.
- If multiple interpretations exist, present them - don't pick silently.
- If a simpler approach exists, say so. Push back when warranted.
- If something is unclear, stop. Name what's confusing. Ask.
### 2. Simplicity First
**Minimum code that solves the problem. Nothing speculative.**
- No features beyond what was asked.
- No abstractions for single-use code.
- No "flexibility" or "configurability" that wasn't requested.
- No error handling for impossible scenarios.
- If you write 200 lines and it could be 50, rewrite it.
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
### 3. Surgical Changes
**Touch only what you must. Clean up only your own mess.**
When editing existing code:
- Don't "improve" adjacent code, comments, or formatting.
- Don't refactor things that aren't broken.
- Match existing style, even if you'd do it differently.
- If you notice unrelated dead code, mention it - don't delete it.
When your changes create orphans:
- Remove imports/variables/functions that YOUR changes made unused.
- Don't remove pre-existing dead code unless asked.
The test: Every changed line should trace directly to the user's request.
### 4. Goal-Driven Execution
**Define success criteria. Loop until verified.**
Transform tasks into verifiable goals:
- "Add validation" β "Write tests for invalid inputs, then make them pass"
- "Fix the bug" β "Write a test that reproduces it, then make it pass"
- "Refactor X" β "Ensure tests pass before and after"
For multi-step tasks, state a brief plan:
```
1. [Step] β verify: [check]
2. [Step] β verify: [check]
3. [Step] β verify: [check]
```
Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification.