AdaptixC2 v2 Extender Development
Develop against the checked-out source, not remembered v1 APIs or the public GitBook. Adaptix v2 is pre-release: pin the exact source revision and re-run the contract gate for every new task.
Verified baseline for this guidance: AdaptixC2 commit 72f20075 on testing-v2.0, github.com/Adaptix-Framework/axc2/v2 v2.0.13, Go 1.26.5.
Start with the contract gate
From <adaptix-root>:
git status --short --branch
git rev-parse HEAD
go -C AdaptixServer list -m github.com/Adaptix-Framework/axc2/v2
go -C AdaptixServer env GOVERSION
go -C AdaptixServer doc github.com/Adaptix-Framework/axc2/v2.PluginAgent
go -C AdaptixServer doc github.com/Adaptix-Framework/axc2/v2.PluginListener
go -C AdaptixServer doc github.com/Adaptix-Framework/axc2/v2.PluginService
Then inspect the implementation that owns the behavior being changed:
rg -n "InitPlugin|plugin.Open|LoadPlugin" AdaptixServer
rg -n "GenerateUI|ListenerUI|InitService|data_handler" AdaptixClient AdaptixServer
rg -n "TsAgentBuild|BuildPayload|GenerateProfiles" AdaptixServer
rg -n "plugin_service_(command|wait)|plugin_(agent|listener)_command" AdaptixClient AdaptixServer
If the revision or module version differs from the baseline, treat every signature and lifecycle statement below as a hypothesis until re-verified.
Choose one extender boundary
| Extender |
Owns |
Does not own |
| Agent |
payload configuration/build, task encoding, beat processing, restored session behavior |
client transport, build-channel close, final artifact delivery |
| Listener |
transport instance lifecycle and inbound agent traffic |
agent protocol semantics beyond delegation to its agent plugin |
| Service |
auxiliary server behavior, optional dock/dialog UI, service messages and bounded RPC |
core agent/listener lifecycle or implicit authorization |
Keep the dependency direction one-way:
AxScript UI -> named bridge call -> plugin boundary -> domain logic -> explicit Teamserver port
Do not let UI JSON, listener configuration, task arguments, or callback bytes select filesystem paths, programs, routes, or storage namespaces without validation. Keep Adaptix types at the boundary and ordinary Go types in domain code.
For boundary ownership, lifecycle state machines, failure semantics, and concurrency, load architecture-and-lifecycle.md.
Route to the narrowest reference
| Task |
Load when |
| plugin-patterns.md |
Implementing Go interfaces, agent restore/build, listener start/stop, service calls, or safe boundary adapters |
| axscript-patterns.md |
Building payload forms, listener forms, service docks/dialogs, event handlers, or UI-to-plugin interactions |
| axscript-api.md |
Checking an exact client bridge method, argument order, return shape, or server stub parity |
| teamserver-api.md |
Selecting Teamserver calls and checking ownership, errors, hooks, endpoints, storage, or messaging semantics |
| generator-details.md |
Creating templates/specs, building, installing, activating, or auditing axtool behavior |
Load the parent first, then only the references required by the current subtask.
Implement a thin vertical slice
- Define the user-visible outcome and the failure state before coding.
- Record the exact Adaptix revision, axc2 module version, Go toolchain, build flags, and shared dependency versions.
- Isolate protocol/build/service logic from
Plugin*, Teamserver, and AxScript adapters.
- Implement one end-to-end path: UI input -> validation -> plugin call -> result/error -> observable UI state.
- Add lifecycle cleanup and concurrency bounds before adding more commands.
- Verify activation from server logs and runtime catalogs; process readiness alone does not prove the extender loaded.
Use explicit schemas at every JSON boundary. Reject unknown operations, missing required fields, oversized inputs, invalid enum values, unsafe paths, and malformed byte frames. Return errors with operation context; never turn an unknown command into a successful zero value.
Non-negotiable runtime rules
- Agent and service
.axs files are evaluated by server-side Goja for command metadata and by the Qt client for real UI behavior; listener .axs is client-only on the verified baseline. Keep top-level code declarative and place UI effects in GenerateUI, ListenerUI, or InitService.
- Agent state must be reconstructible by
AgentRestore. The current server does not retain the callbacks returned by CreateAgent when creating a session.
BuildPayload returns artifact bytes and a filename to its caller. The WebSocket path sends/closes the build channel; the synchronous path returns the artifact directly.
- Listener
Start can fail while the API call still completes with a stopped instance. Make Start restart-safe, Stop idempotent, and verify runtime status.
- Plugin instances are shared. Assume
Call, CallRPC, task handlers, hooks, and build callbacks can overlap.
- A service RPC timeout does not cancel plugin work. Bound work independently and correlate asynchronous replies with a request ID.
- Go plugins cannot be safely unloaded from the process. Registry removal is not resource teardown; design cleanup before supporting runtime unload.
- Treat extender binaries and
axtool specs as trusted code. Do not run unreviewed packages or mutable build inputs.
Wrong-type traps (axc2/v2 v2.0.13)
Verified against axc2/v2@v2.0.13/adaptix_struct.go. Common misuses that compile but corrupt runtime state:
| Field / constant |
Real type |
Trap |
AgentData.Sleep, AgentData.Jitter |
uint (seconds) |
Do not parse from a duration string in-plugin; convert once at the boundary. |
AgentData.Pid, AgentData.Tid |
string |
Format from numeric sources with fmt.Sprintf("%v", ...); do not cast. |
AgentData.Id and every agentId on Teamserver/Call/InternalHandler |
int64 |
Never a string. In JavaScript treat it as opaque string when it may exceed Number.MAX_SAFE_INTEGER. |
OS_MAC (constant 3) |
int |
No OS_MACOS symbol exists. |
PluginService |
interface with Call and CallRPC |
An implementation with only Call will not satisfy the loader type assertion. |
config.yaml -> extender_file |
filename string |
Must equal the Makefile .so output byte-for-byte; a mismatch loads nothing and the server log is the only signal. |
Verification gates
Run focused tests for pure domain code, then compile with the exact Teamserver toolchain and dependency graph. At minimum, exercise:
- valid and malformed boundary input;
- short/truncated frames before indexing or slicing;
- concurrent builds/calls under the race detector where supported;
- create/start -> pause -> resume -> full stop for listeners, plus partial-start failure cleanup;
- create and restored-session behavior for agents;
- success, plugin error, timeout, late result, and disconnect for service UI;
- build failure cleanup and absence of duplicate artifact delivery;
- cold-start activation and runtime catalog/UI visibility.
Before claiming completion:
go test ./...
go test -race ./...
go vet ./...
rg -n "panic\(|log\.Fatal|os\.Exit" .
Use the race gate for Go/domain packages on a supported native target; it does not prove races across the host/plugin boundary. Also inspect the built .so, server profile entry, load logs, command catalogs, and the intended UI in a real client. Report skipped gates and upstream source failures separately from extender failures.
Baseline limitation: commit 72f20075 contains no Go test files, so its go test runs are compile gates only; beacon_listener_dns currently fails that gate at pl_transport.go:375 (undefined: total). Recheck both facts on a newer revision before attributing the failure to an extender change.
Delivery record
Record:
- pinned Adaptix revision, axc2 version, Go version, build flags, and target OS/architecture;
- extender type, init signature, owned resources, cleanup path, and concurrency policy;
- AxScript entry points and bridge calls used;
- package/spec provenance and activation path;
- tests run, runtime evidence, known source limitations, and rollback/restart procedure.
Do not claim transactional install, safe hot reload, automatic timeout cancellation, or UI delivery without direct evidence from the pinned source and a runtime check.
1---2name: adaptixc2-dev3description: Build, review, debug, package, and activate AdaptixC2 v2 extenders and AxScript UI: agent payload builders, listeners, services, plugin calls, lifecycle, axtool specs, source-contract checks, and verification. Use for authorized extension development, not live C2 operation.4license: MIT5---67# AdaptixC2 v2 Extender Development89Develop against the checked-out source, not remembered v1 APIs or the public GitBook. Adaptix v2 is pre-release: pin the exact source revision and re-run the contract gate for every new task.1011Verified baseline for this guidance: AdaptixC2 commit `72f20075` on `testing-v2.0`, `github.com/Adaptix-Framework/axc2/v2` v2.0.13, Go 1.26.5.1213## Start with the contract gate1415From `<adaptix-root>`:1617```bash18git status --short --branch19git rev-parse HEAD20go -C AdaptixServer list -m github.com/Adaptix-Framework/axc2/v221go -C AdaptixServer env GOVERSION22go -C AdaptixServer doc github.com/Adaptix-Framework/axc2/v2.PluginAgent23go -C AdaptixServer doc github.com/Adaptix-Framework/axc2/v2.PluginListener24go -C AdaptixServer doc github.com/Adaptix-Framework/axc2/v2.PluginService25```2627Then inspect the implementation that owns the behavior being changed:2829```bash30rg -n "InitPlugin|plugin.Open|LoadPlugin" AdaptixServer31rg -n "GenerateUI|ListenerUI|InitService|data_handler" AdaptixClient AdaptixServer32rg -n "TsAgentBuild|BuildPayload|GenerateProfiles" AdaptixServer33rg -n "plugin_service_(command|wait)|plugin_(agent|listener)_command" AdaptixClient AdaptixServer34```3536If the revision or module version differs from the baseline, treat every signature and lifecycle statement below as a hypothesis until re-verified.3738## Choose one extender boundary3940| Extender | Owns | Does not own |41|---|---|---|42| Agent | payload configuration/build, task encoding, beat processing, restored session behavior | client transport, build-channel close, final artifact delivery |43| Listener | transport instance lifecycle and inbound agent traffic | agent protocol semantics beyond delegation to its agent plugin |44| Service | auxiliary server behavior, optional dock/dialog UI, service messages and bounded RPC | core agent/listener lifecycle or implicit authorization |4546Keep the dependency direction one-way:4748```text49AxScript UI -> named bridge call -> plugin boundary -> domain logic -> explicit Teamserver port50```5152Do not let UI JSON, listener configuration, task arguments, or callback bytes select filesystem paths, programs, routes, or storage namespaces without validation. Keep Adaptix types at the boundary and ordinary Go types in domain code.5354For boundary ownership, lifecycle state machines, failure semantics, and concurrency, load [architecture-and-lifecycle.md](references/architecture-and-lifecycle.md).5556## Route to the narrowest reference5758| Task | Load when |59|---|---|60| [plugin-patterns.md](references/plugin-patterns.md) | Implementing Go interfaces, agent restore/build, listener start/stop, service calls, or safe boundary adapters |61| [axscript-patterns.md](references/axscript-patterns.md) | Building payload forms, listener forms, service docks/dialogs, event handlers, or UI-to-plugin interactions |62| [axscript-api.md](references/axscript-api.md) | Checking an exact client bridge method, argument order, return shape, or server stub parity |63| [teamserver-api.md](references/teamserver-api.md) | Selecting Teamserver calls and checking ownership, errors, hooks, endpoints, storage, or messaging semantics |64| [generator-details.md](references/generator-details.md) | Creating templates/specs, building, installing, activating, or auditing `axtool` behavior |6566Load the parent first, then only the references required by the current subtask.6768## Implement a thin vertical slice69701. Define the user-visible outcome and the failure state before coding.712. Record the exact Adaptix revision, axc2 module version, Go toolchain, build flags, and shared dependency versions.723. Isolate protocol/build/service logic from `Plugin*`, `Teamserver`, and AxScript adapters.734. Implement one end-to-end path: UI input -> validation -> plugin call -> result/error -> observable UI state.745. Add lifecycle cleanup and concurrency bounds before adding more commands.756. Verify activation from server logs and runtime catalogs; process readiness alone does not prove the extender loaded.7677Use explicit schemas at every JSON boundary. Reject unknown operations, missing required fields, oversized inputs, invalid enum values, unsafe paths, and malformed byte frames. Return errors with operation context; never turn an unknown command into a successful zero value.7879## Non-negotiable runtime rules8081- Agent and service `.axs` files are evaluated by server-side Goja for command metadata and by the Qt client for real UI behavior; listener `.axs` is client-only on the verified baseline. Keep top-level code declarative and place UI effects in `GenerateUI`, `ListenerUI`, or `InitService`.82- Agent state must be reconstructible by `AgentRestore`. The current server does not retain the callbacks returned by `CreateAgent` when creating a session.83- `BuildPayload` returns artifact bytes and a filename to its caller. The WebSocket path sends/closes the build channel; the synchronous path returns the artifact directly.84- Listener `Start` can fail while the API call still completes with a stopped instance. Make `Start` restart-safe, `Stop` idempotent, and verify runtime status.85- Plugin instances are shared. Assume `Call`, `CallRPC`, task handlers, hooks, and build callbacks can overlap.86- A service RPC timeout does not cancel plugin work. Bound work independently and correlate asynchronous replies with a request ID.87- Go plugins cannot be safely unloaded from the process. Registry removal is not resource teardown; design cleanup before supporting runtime unload.88- Treat extender binaries and `axtool` specs as trusted code. Do not run unreviewed packages or mutable build inputs.8990### Wrong-type traps (axc2/v2 v2.0.13)9192Verified against `axc2/v2@v2.0.13/adaptix_struct.go`. Common misuses that compile but corrupt runtime state:9394| Field / constant | Real type | Trap |95|---|---|---|96| `AgentData.Sleep`, `AgentData.Jitter` | `uint` (seconds) | Do not parse from a duration string in-plugin; convert once at the boundary. |97| `AgentData.Pid`, `AgentData.Tid` | `string` | Format from numeric sources with `fmt.Sprintf("%v", ...)`; do not cast. |98| `AgentData.Id` and every `agentId` on `Teamserver`/`Call`/`InternalHandler` | `int64` | Never a string. In JavaScript treat it as opaque string when it may exceed `Number.MAX_SAFE_INTEGER`. |99| `OS_MAC` (constant `3`) | `int` | No `OS_MACOS` symbol exists. |100| `PluginService` | interface with `Call` **and** `CallRPC` | An implementation with only `Call` will not satisfy the loader type assertion. |101| `config.yaml` -> `extender_file` | filename string | Must equal the Makefile `.so` output byte-for-byte; a mismatch loads nothing and the server log is the only signal. |102103## Verification gates104105Run focused tests for pure domain code, then compile with the exact Teamserver toolchain and dependency graph. At minimum, exercise:106107- valid and malformed boundary input;108- short/truncated frames before indexing or slicing;109- concurrent builds/calls under the race detector where supported;110- create/start -> pause -> resume -> full stop for listeners, plus partial-start failure cleanup;111- create and restored-session behavior for agents;112- success, plugin error, timeout, late result, and disconnect for service UI;113- build failure cleanup and absence of duplicate artifact delivery;114- cold-start activation and runtime catalog/UI visibility.115116Before claiming completion:117118```bash119go test ./...120go test -race ./...121go vet ./...122rg -n "panic\(|log\.Fatal|os\.Exit" .123```124125Use the race gate for Go/domain packages on a supported native target; it does not prove races across the host/plugin boundary. Also inspect the built `.so`, server profile entry, load logs, command catalogs, and the intended UI in a real client. Report skipped gates and upstream source failures separately from extender failures.126127Baseline limitation: commit `72f20075` contains no Go test files, so its `go test` runs are compile gates only; `beacon_listener_dns` currently fails that gate at `pl_transport.go:375` (`undefined: total`). Recheck both facts on a newer revision before attributing the failure to an extender change.128129## Delivery record130131Record:132133- pinned Adaptix revision, axc2 version, Go version, build flags, and target OS/architecture;134- extender type, init signature, owned resources, cleanup path, and concurrency policy;135- AxScript entry points and bridge calls used;136- package/spec provenance and activation path;137- tests run, runtime evidence, known source limitations, and rollback/restart procedure.138139Do not claim transactional install, safe hot reload, automatic timeout cancellation, or UI delivery without direct evidence from the pinned source and a runtime check.