aproxy/CLAUDE.md
2025-11-22 01:13:47 -03:00

135 lines
3.9 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
aproxy is an Activity Pub Reverse Proxy Framework built on OpenResty (NGINX + LuaJIT). It provides a modular script system to add filtering and protection capabilities in front of ActivityPub implementations.
## Development Commands
### Testing
```sh
# Install test dependencies (only needed once)
make testdeps
eval (luarocks-5.1 path --bin)
# Run test suite
make test
```
The test suite uses luajit and luaunit for testing. Tests are located in the `tests/` directory.
## Architecture
### Core Components
- **main.lua**: Entry point with two hooks:
- `init()`: Validates configuration at startup
- `access()`: Called on every request to execute the filter chain
- **config.lua**: Configuration system
- Searches for `conf.lua` in paths: `.;/etc/aproxy` (or `$APROXY_CONFIG_PATH`)
- Provides schema validation framework
- Schema supports types: `string`, `number`, `table`, `list` (array of values)
- **ctx.lua**: Request context and filter chain executor
- Loads scripts from config and builds `compiled_chain`
- Matches request URIs against script PCRE regexes
- Executes matching callbacks sequentially
- If any callback returns a status code, request is terminated with that response
- **util.lua**: Lua standard library extensions
- `table.readonly()`: Create read-only table wrapper
- `table.pprint()`: Pretty-print nested tables
- `string.split()`: Split strings by separator
### Script Module Structure
Scripts live in `scripts/` and must export:
```lua
return {
name = 'ModuleName',
author = 'email',
title = 'Short Title',
description = [[Long description]],
version = 1,
-- Called once at startup with config
-- Return value becomes module state
init = function(cfg)
return state
end,
-- Map of PCRE regex patterns to callback functions
callbacks = {
['/api/path'] = function(cfg, state)
-- Return nil to allow request
-- Return status_code, body to block request
return nil
end
},
-- Schema for validating this module's config
config = {
['field_name'] = {
type = 'string|number|table|list',
schema = {...}, -- for table/list types
description = 'field description'
}
}
}
```
**Important**: Module configs are made read-only via `table.readonly()` before being passed to callbacks. Attempting to modify them will error.
### Request Flow
1. OpenResty calls `aproxy.main.access()` on each request
2. `ctx:onRequest()` iterates through `compiled_chain`
3. For each script, check if request URI matches any callback regex
4. Execute matching callbacks with their config and state
5. If callback returns `(status_code, body)`, terminate request with that response
6. If callback returns `nil`, continue to next callback
7. If no callbacks block the request, pass through to backend
### Testing Framework
Tests use a mock `ngx` object (created by `createNgx()` in test.lua) that simulates OpenResty's API:
- `ngx.var.uri`: Request URI
- `ngx.req.get_uri_args()`: Query parameters
- `ngx.req.get_headers()`: Request headers
- `ngx.status`, `ngx.say()`, `ngx.exit()`: Response control
- `ngx.re.match()`: PCRE regex matching via rex_pcre2
Use `setupTest(module_path, config)` to initialize a module for testing and `setupFakeRequest(path, options)` to simulate requests.
To run the test suite:
```sh
# needed only once ever to setup the environment
make testdeps
# run once on any new shell
eval (luarocks-5.1 path --bin)
# actually run suite
make test
```
## Configuration
The default config file (`conf.lua`) structure:
```lua
return {
version = 1,
wantedScripts = {
['script_name'] = {
-- script-specific config matching its schema
}
}
}
```
Scripts are loaded from `scripts/{script_name}.lua` based on keys in `wantedScripts`.