diff --git a/README.md b/README.md
index c989ee4..71ede9a 100644
--- a/README.md
+++ b/README.md
@@ -1,152 +1,95 @@
-# OAShield
-
-
-
-## Overview
-The OAShield (pronounced like "away shield" or /əˈweɪ ʃild/) project provides a generator for creating Web Application Firewall (WAF) configuration files based on OpenAPI specifications. By leveraging the OpenAPI spec, the generated rules will only allow valid API calls, enhancing security by disallowing any undefined operations.
-
-OAShield generates SecLang rules for both OWASP ModSecurity v3 (libmodsecurity) and [Coraza](https://coraza.io), selectable via the `engineFlavor` option (see below).
-
-## Description
-Traditional WAF rules rely on pattern matching to detect and block suspicious requests. This generator takes a different approach by using the OpenAPI specification to define what constitutes a valid request. For example, if an API specification does not define a POST method for a particular endpoint, the generated rules will disallow any POST requests to that endpoint. These rules can be deployed alongside the API or in a sidecar to provide an additional security layer.
-
-## Usage Instructions
-
-
-### Build the Project:
-1. **Prerequisites:**
- - Java 8 or later
- - Maven 3.6.0 or later
-2. Clone the repository:
- ```
- git clone https://github.com/cognitivegears/oashield.git
- ```
-3. Build the generator project including the CLI jar:
- ```
- cd oashield
- mvn package -P build-cli-jar
- ```
-This will produce a JAR file `oashield-cli.jar` in the `target` directory.
-
-### OR Download the CLI Jar:
-
- - Download the latest release of oashield-cli.jar from the [Releases](https://github.com/cognitivegears/oashield/releases) page.
-
-
-### Run the generator:
- - Use the following command to generate the ModSecurity configuration:
- ```
- java -cp target/oashield-cli.jar org.openapitools.codegen.OpenAPIGenerator generate -g modsecurity3 -i /path/to/openapi.yaml -o /path/to/output/dir
- ```
- Replace `/path/to/openapi.yaml`, and `/path/to/output/dir` with the appropriate paths.
-
- - For Windows users, use `;` instead of `:` in the classpath:
- ```
- java -cp target/oashield-cli.jar org.openapitools.codegen.OpenAPIGenerator generate -g modsecurity3 -i /path/to/openapi.yaml -o /path/to/output/dir
- ```
-
-### Engine flavors
-
-Most generated rules are identical for both engines; the flavor selects how JSON
-request bodies are validated:
-
-| Flavor | JSON body validation |
-|---|---|
-| `modsecurity3` (default) | Per-field rules generated from the OpenAPI schema: required-property presence, per-property type patterns, numeric minimum/maximum, and an `ARGS_NAMES` allowlist rejecting undeclared properties (`additionalProperties`) |
-| `coraza` | The same per-field rules, plus a `@validateSchema` rule validating the raw body against the generated JSON Schema. `@validateSchema` is Coraza-only — ModSecurity v3's operator of the same name is XSD/XML-only |
-
-Select the flavor with additional properties:
-
-```
-... generate -g modsecurity3 -i api.yaml -o out --additional-properties engineFlavor=coraza,schemaRulePath=rules/schema.json
-```
+
-Available options:
-- `engineFlavor` — `modsecurity3` (default) or `coraza`
-- `validateBodySchema` — emit request-body validation rules (default `true`)
-- `generateJsonSchema` — emit the JSON Schema file (default `true`)
-- `jsonSchemaOutputFile` — schema file name (default `schema.json`)
-- `schemaRulePath` — the schema path written inside the `@validateSchema` rule; Coraza resolves it relative to the server process working directory, not the rules directory (default: same as `jsonSchemaOutputFile`)
+
-Known limitations of per-field body validation (Coraza's `@validateSchema` additionally covers these):
-- Values are validated after the engine flattens JSON to strings, so a JSON number where a free-form string is expected (e.g. `"name": 123`) is not distinguishable.
-- `required` is enforced for object properties (nested ones only when their parent object is present, per JSON Schema semantics), but not per array element; an empty array satisfies a required array property only on the `coraza` flavor.
-- Model nesting is flattened to a depth of 5 levels; deeper properties are covered only by the unknown-property allowlist.
-- Numeric minimum/maximum on *path* parameters is enforced only lexically via the embedded path pattern.
-
-### Deploy the generated rules:
-
-Copy the generated ModSecurity configuration files from the output path (i.e. `/path/to/output/dir`) to your ModSecurity setup.
-
-## Testing
-
-### Unit Tests
+# OAShield
-Run the unit tests with:
+**Turn your OpenAPI spec into a Web Application Firewall that only allows valid API calls.**
-```bash
-mvn test
-```
+[](https://github.com/cognitivegears/oashield/actions/workflows/ci.yml)
+[](https://github.com/cognitivegears/oashield/actions/workflows/maven-tests.yml)
+[](https://github.com/cognitivegears/oashield/actions/workflows/maven-build-cli.yml)
+[](LICENSE.md)
+[](https://github.com/cognitivegears/oashield/releases)
-### Integration Tests
+[Website](https://oashield.com) · [Getting Started](docs/getting-started.md) · [Configuration](docs/configuration.md) · [Releases](https://github.com/cognitivegears/oashield/releases)
-Integration tests validate the generated rules against real WAF engines — every scenario runs against both Coraza (`ghcr.io/cognitivegears/coraza-validate-server`) and OWASP ModSecurity v3 (`owasp/modsecurity-crs:nginx`, CRS disabled, oashield rules only). These tests require Docker to be installed and running.
+
-Note: the `owasp/modsecurity-crs:nginx` image currently publishes only `linux/386`; on other architectures (e.g. Apple Silicon) pull it once with:
+---
-```bash
-docker pull --platform linux/386 owasp/modsecurity-crs:nginx
-```
+## What is OAShield?
-Run the integration tests with:
+OAShield (pronounced like "away shield", /əˈweɪ ʃild/) generates Web Application
+Firewall (WAF) rules directly from your OpenAPI specification. The generated
+rules act as a **positive security model**: they permit exactly the requests
+your API defines and deny everything else.
-```bash
-mvn verify
-```
+It produces standard [SecLang](https://coraza.io/docs/seclang/) rules for both
+**[OWASP ModSecurity v3](https://github.com/owasp-modsecurity/ModSecurity)**
+(libmodsecurity) and **[Coraza](https://coraza.io)**, selectable with a single
+option.
-or specifically:
+## Why OAShield?
-```bash
-mvn failsafe:integration-test failsafe:verify
-```
+Traditional WAFs rely on pattern matching — a blocklist that tries to recognize
+known-bad requests. That leaves a gap: anything the patterns haven't seen gets
+through.
-The integration tests:
-- Generate ModSecurity rules from sample OpenAPI specifications
-- Launch a Coraza WAF container with the generated rules
-- Send HTTP requests to validate that the rules correctly enforce the API specification
-- Test various scenarios including parameter validation, path validation, and method validation
+OAShield flips the model. Instead of guessing what's malicious, it uses your
+OpenAPI spec as the definition of what's **valid**:
-Test reports are generated in the `target/extent-reports` directory.
+- **Deny by default.** If your spec doesn't define a `POST /orders`, the rules
+ reject every `POST /orders` request — no signature required.
+- **Enforce your contract.** Undeclared endpoints, methods, parameters, and
+ request-body fields are blocked. Type and range constraints from the schema
+ are checked.
+- **Deploy anywhere SecLang runs.** Ship the rules alongside your API or in a
+ sidecar for an extra layer that mirrors your API surface exactly.
-#### Note for ARM Architecture Users (Mac M1/M2/M3)
+Your OpenAPI spec is already the source of truth for your API. OAShield makes it
+the source of truth for your perimeter too. Curious what the rules look like?
+See [How It Works](docs/how-it-works.md).
-Integration tests now support ARM-based systems (like Mac M1/M2/M3) as the Docker container is available for ARM architecture. You can run the integration tests normally on ARM systems:
+## Quick Start
```bash
-mvn verify
+# 1. Get the CLI (or download from Releases)
+git clone https://github.com/cognitivegears/oashield.git
+cd oashield
+mvn package -P build-cli-jar
+
+# 2. Generate rules from your spec
+java -cp target/oashield-cli.jar org.openapitools.codegen.OpenAPIGenerator \
+ generate -g modsecurity3 \
+ -i samples/petstore.yaml \
+ -o output/
+
+# 3. Deploy the generated .conf files to your ModSecurity or Coraza setup
```
-If you want to skip the HTTP calls for any reason, you can use:
-
-```bash
-mvn verify -Dskip.http.calls=true
-```
+Full walkthrough: **[Getting Started](docs/getting-started.md)**.
-You can also run tests with relaxed validation for status codes (useful when running on systems where some rules may not work correctly yet):
+## Documentation
-```bash
-mvn verify -Dskip.strict.validation=true
-```
+| Guide | What's inside |
+|---|---|
+| [Getting Started](docs/getting-started.md) | Install, generate rules, and deploy them |
+| [How It Works](docs/how-it-works.md) | What the generated rules do, block by block |
+| [Configuration](docs/configuration.md) | Options, engine flavors, and validation limitations |
+| [Integration Testing](docs/integration-testing.md) | Running the test suite against real WAF engines |
-### Continuous Integration
+## Examples
-This project uses GitHub Actions for continuous integration:
+Sample OpenAPI specs live in [`samples/`](samples), with pre-generated rules in
+[`samples/output/petstore/`](samples/output/petstore) so you can see what
+OAShield produces before running it yourself.
-- **OAShield Tests**: Runs on all PRs and pushes to the main branch, executing unit and integration tests.
-- **OAShield CLI Build**: Builds the CLI JAR and verifies its functionality.
-- **OAShield CI**: Comprehensive workflow that builds, tests, and verifies both the library and CLI.
+## Contributing
-The GitHub Actions workflows automatically skip HTTP calls during testing to ensure compatibility with the CI environment.
+Issues and pull requests are welcome. Please run `mvn test` before submitting;
+see [Integration Testing](docs/integration-testing.md) for the full suite.
## License
-This project is licensed under the Apache License 2.0. See the [LICENSE](LICENSE.md) file for details.
+
+Licensed under the Apache License 2.0. See [LICENSE.md](LICENSE.md).
diff --git a/docs/configuration.md b/docs/configuration.md
new file mode 100644
index 0000000..99f08ec
--- /dev/null
+++ b/docs/configuration.md
@@ -0,0 +1,49 @@
+# Configuration
+
+OAShield is configured through the OpenAPI Generator `--additional-properties`
+flag. This page documents the available options, the two engine flavors, and
+the known limitations of request-body validation.
+
+## Options
+
+| Option | Default | Description |
+|---|---|---|
+| `engineFlavor` | `modsecurity3` | Target engine: `modsecurity3` or `coraza` |
+| `validateBodySchema` | `true` | Emit request-body validation rules |
+| `generateJsonSchema` | `true` | Emit the JSON Schema file |
+| `jsonSchemaOutputFile` | `schema.json` | JSON Schema file name |
+| `schemaRulePath` | same as `jsonSchemaOutputFile` | Schema path written inside the `@validateSchema` rule. Coraza resolves it relative to the **server process working directory**, not the rules directory |
+
+Pass them comma-separated:
+
+```bash
+... generate -g modsecurity3 -i api.yaml -o out \
+ --additional-properties engineFlavor=coraza,schemaRulePath=rules/schema.json
+```
+
+## Engine flavors
+
+Most generated rules are identical across both engines — the flavor only
+changes how JSON request bodies are validated.
+
+| Flavor | JSON body validation |
+|---|---|
+| `modsecurity3` (default) | Per-field rules generated from the OpenAPI schema: required-property presence, per-property type patterns, numeric minimum/maximum, and an `ARGS_NAMES` allowlist that rejects undeclared properties (`additionalProperties`) |
+| `coraza` | The same per-field rules **plus** a `@validateSchema` rule that validates the raw body against the generated JSON Schema. `@validateSchema` is Coraza-only — ModSecurity v3's operator of the same name is XSD/XML-only |
+
+## Limitations of per-field body validation
+
+These apply to the `modsecurity3` flavor's per-field checks. Coraza's
+`@validateSchema` additionally covers all of them:
+
+- **Type coercion:** values are validated after the engine flattens JSON to
+ strings, so a JSON number where a free-form string is expected
+ (e.g. `"name": 123`) is not distinguishable.
+- **`required` scope:** enforced for object properties (nested ones only when
+ their parent object is present, per JSON Schema semantics), but not per array
+ element. An empty array satisfies a required array property only on the
+ `coraza` flavor.
+- **Nesting depth:** model nesting is flattened to 5 levels; deeper properties
+ are covered only by the unknown-property allowlist.
+- **Numeric bounds on path parameters:** `minimum`/`maximum` on *path*
+ parameters is enforced only lexically, via the embedded path pattern.
diff --git a/docs/getting-started.md b/docs/getting-started.md
new file mode 100644
index 0000000..b136910
--- /dev/null
+++ b/docs/getting-started.md
@@ -0,0 +1,73 @@
+# Getting Started
+
+This guide walks you from zero to a set of deployable WAF rules generated from
+your OpenAPI specification.
+
+## Prerequisites
+
+- Java 8 or later (Java 11+ recommended)
+- Maven 3.6.0 or later — only needed if you build from source
+
+## 1. Get the CLI
+
+### Option A — Download a release (fastest)
+
+Download the latest `oashield-cli.jar` from the
+[Releases](https://github.com/cognitivegears/oashield/releases) page.
+
+### Option B — Build from source
+
+```bash
+git clone https://github.com/cognitivegears/oashield.git
+cd oashield
+mvn package -P build-cli-jar
+```
+
+This produces `target/oashield-cli.jar`.
+
+## 2. Generate rules
+
+```bash
+java -cp oashield-cli.jar org.openapitools.codegen.OpenAPIGenerator \
+ generate -g modsecurity3 \
+ -i /path/to/openapi.yaml \
+ -o /path/to/output/dir
+```
+
+Replace `-i` with the path to your OpenAPI spec and `-o` with the directory
+where the generated `.conf` rules should be written.
+
+> **Windows:** the same command works; if you add multiple classpath entries,
+> separate them with `;` instead of `:`.
+
+To validate JSON request bodies against the OpenAPI schema (and to target
+[Coraza](https://coraza.io)), select the engine flavor:
+
+```bash
+java -cp oashield-cli.jar org.openapitools.codegen.OpenAPIGenerator \
+ generate -g modsecurity3 \
+ -i /path/to/openapi.yaml \
+ -o /path/to/output/dir \
+ --additional-properties engineFlavor=coraza,schemaRulePath=rules/schema.json
+```
+
+See [Configuration](configuration.md) for every option and the differences
+between engine flavors.
+
+## 3. Deploy the rules
+
+Copy the generated `.conf` files from your output directory into your
+ModSecurity or Coraza setup, alongside your API or in a sidecar. The rules are
+standard SecLang, so they load the same way any other rule file does.
+
+For a worked example, see the sample specs in [`samples/`](../samples) and the
+pre-generated output in [`samples/output/petstore/`](../samples/output/petstore).
+
+## Running unit tests
+
+```bash
+mvn test
+```
+
+For integration tests against real WAF engines, see
+[Integration Testing](integration-testing.md).
diff --git a/docs/how-it-works.md b/docs/how-it-works.md
new file mode 100644
index 0000000..67f4367
--- /dev/null
+++ b/docs/how-it-works.md
@@ -0,0 +1,152 @@
+# How It Works
+
+OAShield turns an OpenAPI specification into a **positive security model** — a
+set of WAF rules that permit exactly the requests your API declares and deny
+everything else. This page explains what the generated rules actually do.
+
+## The big picture
+
+A conventional WAF is a *blocklist*: it inspects each request against
+signatures of known attacks and blocks matches. Anything the signatures don't
+recognize passes through.
+
+OAShield builds an *allowlist* instead. Your OpenAPI spec already describes
+every legitimate endpoint, method, parameter, and body field. OAShield compiles
+that description into [SecLang](https://coraza.io/docs/seclang/) rules that:
+
+1. Match the incoming request against each declared operation.
+2. Validate the request's parameters and body against that operation's schema.
+3. **Deny by default** if no operation matched, or if validation failed.
+
+The result runs on any engine that speaks SecLang —
+[OWASP ModSecurity v3](https://github.com/owasp-modsecurity/ModSecurity) or
+[Coraza](https://coraza.io).
+
+## Anatomy of the generated rules
+
+OAShield emits one `.conf` file per API tag (e.g. `PetApi.conf`,
+`StoreApi.conf`). Every file is a sequence of **operation blocks** followed by a
+single **default-deny** at the end. The examples below are taken verbatim from
+[`samples/output/petstore/`](../samples/output/petstore).
+
+### 1. Operation matching (skip-if-not-this-operation)
+
+Each block begins by asking "is this request for *this* operation?" If not, it
+skips ahead to the block's end marker and moves on. Three checks gate the block —
+path, exact-path (no extra segments), and method:
+
+```seclang
+# addPet: POST /pet
+SecRule REQUEST_URI "!@restpath /pet" "id:4200001,phase:2,pass,nolog,skipAfter:END_addPet"
+SecRule REQUEST_URI "!@rx ^/pet(\?.*)?$" "id:4200002,phase:2,pass,nolog,skipAfter:END_addPet"
+SecRule REQUEST_METHOD "!@within POST" "id:4200003,phase:2,pass,nolog,skipAfter:END_addPet"
+```
+
+`@restpath` matches the OpenAPI path template (including `{petId}`-style
+placeholders); the `@rx` line rejects requests with extra trailing segments so
+`/pet/extra` doesn't slip into the `/pet` block. If any check fails, `skipAfter`
+jumps to `SecMarker END_addPet` — this request isn't for `addPet`.
+
+### 2. Parameter validation
+
+Once a request is confirmed to belong to an operation, its parameters are
+checked against the schema. Values that don't match are denied outright; a
+`skipAfter:FAILED_API_CHECKS` sends them to the default-deny at the end of the
+file:
+
+```seclang
+# path parameter petId, typed as integer
+SecRule ARGS_PATH:petId "!@rx ^[0-9]{1,19}$" "id:4210021,phase:2,deny,status:403,msg:'Forbidden parameter value detected',...,skipAfter:FAILED_API_CHECKS"
+SecRule &ARGS_PATH:petId "@gt 1" "id:4210022,phase:2,deny,status:403,msg:'Multiple values for non-array parameter',...,skipAfter:FAILED_API_CHECKS"
+
+# enum query parameter status
+SecRule ARGS_GET:status "!@rx ^(available|pending|sold)$" "id:4210063,phase:2,deny,status:403,...,skipAfter:FAILED_API_CHECKS"
+```
+
+Each parameter contributes two kinds of rule: a **value** check (`@rx` derived
+from the schema's type, enum, pattern, or numeric bounds) and a **cardinality**
+check (`&ARGS…` counts occurrences, enforcing that a non-array parameter appears
+exactly once).
+
+### 3. Unknown-parameter allowlist
+
+After the declared parameters are validated, anything *not* declared is
+rejected. The allowlist regex lists the permitted names; a request carrying any
+other parameter is blocked:
+
+```seclang
+# operation with no query params — any query arg is unknown
+SecRule ARGS_GET_NAMES "@rx ^.+$" "id:4200007,phase:2,block,msg:'Unknown parameter detected',...,skipAfter:FAILED_API_CHECKS"
+
+# operation that declares `status` — anything else is unknown
+SecRule ARGS_GET_NAMES "!@rx ^(status)$" "id:4200046,phase:2,block,msg:'Unknown parameter detected',...,skipAfter:FAILED_API_CHECKS"
+```
+
+### 4. Operation passed — allow and exit the block
+
+If the request survived every check, it's a valid call to this operation. The
+block allows it and closes with its end marker:
+
+```seclang
+SecAction "phase:2,allow:request,id:4200008"
+SecMarker END_addPet
+```
+
+### 5. Default deny
+
+At the very end of the file sits the target of every `FAILED_API_CHECKS` jump,
+and the fall-through for any request that matched no operation at all:
+
+```seclang
+# For anything else, deny by default
+SecMarker FAILED_API_CHECKS
+SecAction "id:4220001,log,auditlog,block,phase:2,msg:'Unknown API endpoint'"
+```
+
+This is what makes the model *positive*: reaching the end of the file without an
+explicit `allow` means the request is denied.
+
+## Request-body validation
+
+Parameters cover the query string, path, and form fields. JSON request bodies
+are handled separately, and this is where the two [engine flavors](configuration.md)
+differ:
+
+- **`modsecurity3` (default)** — per-field rules generated from the schema:
+ required-property presence, per-property type patterns, numeric
+ `minimum`/`maximum`, and an `ARGS_NAMES` allowlist that rejects undeclared
+ properties. This works on both engines because it uses only standard operators.
+
+- **`coraza`** — everything above **plus** a `@validateSchema` rule that
+ validates the raw body against a generated JSON Schema file. `@validateSchema`
+ is Coraza-only; ModSecurity v3's operator of the same name handles XSD/XML,
+ not JSON.
+
+Because the per-field approach runs *after* the engine flattens JSON into
+string parameters, it has some inherent limits (type coercion, array-element
+`required`, deep nesting). Those are enumerated in
+[Configuration → Limitations](configuration.md#limitations-of-per-field-body-validation),
+and are exactly the cases Coraza's `@validateSchema` covers in full.
+
+## Why per-tag files and phase 2?
+
+- **Phase 2** is ModSecurity's request-body phase — by then the URI, method,
+ query args, and body are all available, so a single pass can validate the
+ whole request.
+- **One file per tag** keeps the rules readable and lets you deploy or review
+ subsets of your API independently. Load them all together and the final
+ default-deny in each still applies to its own operations.
+
+## See it yourself
+
+The fastest way to build intuition is to generate rules for a spec you know and
+read the output:
+
+```bash
+java -cp target/oashield-cli.jar org.openapitools.codegen.OpenAPIGenerator \
+ generate -g modsecurity3 -i samples/petstore.yaml -o output/
+```
+
+Then open `output/PetApi.conf` and follow the blocks top to bottom. The
+[Petstore sample output](../samples/output/petstore) is already checked in if
+you'd rather just browse.
diff --git a/INTEGRATION_TESTING.md b/docs/integration-testing.md
similarity index 93%
rename from INTEGRATION_TESTING.md
rename to docs/integration-testing.md
index a9406ba..bebe667 100644
--- a/INTEGRATION_TESTING.md
+++ b/docs/integration-testing.md
@@ -62,7 +62,7 @@ docker info
1. Clone the repository:
```bash
- git clone https://github.com/your-org/oashield.git
+ git clone https://github.com/cognitivegears/oashield.git
cd oashield
```
2. Build and run all integration tests:
@@ -220,5 +220,15 @@ mvn test -Dcontainer.image=ghcr.io/myorg/custom-coraza:1.0
mvn test -Doutput.directory.base=/tmp/oashield-results
```
---
-_Documentation generated on 2025-06-24_
+## Continuous Integration
+
+This project uses GitHub Actions:
+
+- **OAShield Tests** — runs on all PRs and pushes to `main`, executing unit and
+ integration tests.
+- **OAShield CLI Build** — builds the CLI JAR and verifies it.
+- **OAShield CI** — comprehensive workflow that builds, tests, and verifies both
+ the library and the CLI.
+
+CI runs skip HTTP calls (`skip.http.calls=true`) for compatibility with the CI
+environment.