> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arupa.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Routing

> Understand Arupa's shared path-pattern and route-selection rules.

Arupa uses one path-pattern implementation for Kernel access rules and plugin
resource registration. The implementation lives in [`internal/netx/path.go`](https://github.com/SteelDrEgg/Arupa/blob/main/internal/netx/path.go) and
provides two operations:

* `ValidatePathPattern` checks whether a pattern is valid; and
* `MatchPathPattern` checks whether a request path matches that pattern.

This is a small path-pattern language. It is not a wildcard, glob, or regular
expression matcher.

## Valid patterns

A pattern must:

* be non-empty; and
* start with `/`.

Use a trailing `/` when you want to match a path subtree:

```text theme={null}
valid:   /api/items
valid:   /api/
invalid: /api/*
invalid: api/items
```

Characters such as `*` or `?` do not create wildcard behavior. Unless the
pattern ends in `/`, the entire pattern is compared as a literal path.

## Matching rules

### Exact paths

A pattern that does not end in `/` matches only the same path:

| Pattern      | Matches      | Does not match                |
| ------------ | ------------ | ----------------------------- |
| `/api/items` | `/api/items` | `/api/items/`, `/api/items/1` |
| `/health`    | `/health`    | `/healthz`                    |

The trailing slash is significant. `/api/items` and `/api/items/` are
different exact paths.

### Subtrees

A pattern ending in `/` matches paths that begin with that pattern:

```text theme={null}
pattern: /api/

matches:     /api/
             /api/items
             /api/items/1

does not match: /api
```

The parent path without the trailing slash is not part of the subtree. If you
need both `/api` and its children, define separate patterns.

### The root path

`/` is handled specially because different resource types use it differently:

| Caller                 | Root behavior                   |
| ---------------------- | ------------------------------- |
| `Route.Allow`          | `/` matches only `/`            |
| Plugin HTTP route      | `/` matches only `/`            |
| Static directory mount | `/` matches every absolute path |

The first two callers use `RootPathExact`. Static mounts use
`RootPathSubtree`, so a static directory mounted at `/` can serve paths below
the root.

## Choosing between matching patterns

When several patterns match a path, Arupa selects the longest matching pattern.
It does not merge the rules or group lists from all matching patterns.

For example, given these host access rules:

```toml theme={null}
[Route.Allow]
  "/api/" = ["staff"]
  "/api/admin/" = ["root"]
```

`/api/admin/users` matches both patterns, but `/api/admin/` wins because it is
longer. Only the `root` group rule is evaluated for that request.

This same longest-pattern rule is used when the plugin router selects an HTTP
route or a static mount.

## Host access rules

`Route.Allow` matches the request's URL path before the request reaches a host
handler or plugin handler. Configuration validation applies the same pattern
rules described above. See [Access control](./config-access)
for how the selected route's group list is evaluated against the authenticated
user.

The query string is not part of path matching. For example, a request for
`/api/items?limit=10` is matched using `/api/items`.

## Plugin HTTP routes

Plugins use the same path patterns when they register HTTP routes. After the
router selects the longest matching path pattern, it selects a handler by HTTP
method:

* an explicitly declared method such as `GET` or `POST` is matched
  case-insensitively after normalization;
* an empty method is a handler for any method; and
* an explicit method takes precedence over an empty-method handler for the
  selected path.

If a path matches but no registered method matches, Arupa returns `405 Method
Not Allowed` and includes the available methods in the `Allow` response header.

Routes with the same path and conflicting methods cannot be registered by
different plugins. A methodless route conflicts with every method at that same
path.

## Static mounts

Static directory mounts use subtree matching. The Kernel normalizes a directory
mount to a pattern ending in `/`, so a mount configured at `/assets` serves the
`/assets/` subtree.

Static file mounts use exact matching and must not end in `/`. A static file
mount at `/favicon.ico` therefore serves that path only.

Static mounts and plugin HTTP routes are both considered by the plugin router.
The router chooses the longest matching pattern; when a static mount and HTTP
route have the same matching length, the static mount is selected.
