aproxy/CLAUDE.md
2025-11-23 17:48:57 -03:00

4 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 for project setup)
make testdeps

# run this to setup the PATH so that it works
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:

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:

# 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.