3.9 KiB
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
# 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 startupaccess(): Called on every request to execute the filter chain
-
config.lua: Configuration system
- Searches for
conf.luain paths:.;/etc/aproxy(or$APROXY_CONFIG_PATH) - Provides schema validation framework
- Schema supports types:
string,number,table,list(array of values)
- Searches for
-
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
- Loads scripts from config and builds
-
util.lua: Lua standard library extensions
table.readonly(): Create read-only table wrappertable.pprint(): Pretty-print nested tablesstring.split(): Split strings by separator
Script Module Structure
Scripts live in scripts/ and must export:
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
- OpenResty calls
aproxy.main.access()on each request ctx:onRequest()iterates throughcompiled_chain- For each script, check if request URI matches any callback regex
- Execute matching callbacks with their config and state
- If callback returns
(status_code, body), terminate request with that response - If callback returns
nil, continue to next callback - 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 URIngx.req.get_uri_args(): Query parametersngx.req.get_headers(): Request headersngx.status,ngx.say(),ngx.exit(): Response controlngx.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:
# 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:
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.