Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
f6bd9ef
Add complete Control management suite
BenCodez Aug 30, 2026
c8a690d
Fix Codex review findings
BenCodez Aug 30, 2026
12c4d65
Fix latest Codex review findings
BenCodez Aug 30, 2026
afdaf53
Fix inspection regression test import
BenCodez Aug 30, 2026
aa38364
Fix fresh Codex review findings
BenCodez Aug 30, 2026
91c2e07
Fix latest Codex security findings
BenCodez Aug 30, 2026
da4802a
Reject duplicate operation journal fields
BenCodez Aug 30, 2026
379bbd6
Preserve restart warnings beyond history window
BenCodez Aug 30, 2026
36c8000
Serialize vote logging applies per target
BenCodez Aug 30, 2026
2ef69d9
Require sessions for completed journal nodes
BenCodez Aug 30, 2026
5d221b9
Preserve interrupted journal state on recovery
BenCodez Aug 30, 2026
b1bebdf
Bind inspection results to claimed session
BenCodez Aug 30, 2026
a264277
Reject duplicate journal operation IDs
BenCodez Aug 30, 2026
aae0f85
Persist automatic cancellations and reset snapshots
BenCodez Aug 30, 2026
81c205b
Allow previewing empty snapshot restores
BenCodez Aug 30, 2026
e8a2fae
Fix durable cancellation rollback and snapshot errors
BenCodez Aug 30, 2026
528e2ce
Make task completion persistence transactional
BenCodez Aug 30, 2026
0fe4b8f
Publish snapshots before retention eviction
BenCodez Aug 30, 2026
dcb438f
Fix snapshot recovery and reconnect state
BenCodez Aug 30, 2026
5154d3f
Bind operation results and conflicts correctly
BenCodez Aug 30, 2026
99c6550
Reject truncated rewards and excess snapshots
BenCodez Aug 30, 2026
dd7070f
Enforce aggregate snapshot retention
BenCodez Aug 30, 2026
166019e
Invalidate stale inspections and duplicate snapshots
BenCodez Aug 30, 2026
4016b41
Restore retained state on persistence failures
BenCodez Aug 30, 2026
6ce49ee
Harden apply rollback and restart markers
BenCodez Aug 30, 2026
b78c5f2
Preserve runtime markers and drift isolation
BenCodez Aug 30, 2026
902164e
Bound vote logging restart markers
BenCodez Aug 30, 2026
fc60162
Isolate drift reads and logout controls
BenCodez Aug 30, 2026
ad9db3a
Recover snapshot transactions and preserve restart entries
BenCodez Aug 30, 2026
c48bd3d
Harden snapshot recovery and restore loading
BenCodez Aug 30, 2026
3a7fc40
Fix recovery trimming and restart warnings
BenCodez Aug 30, 2026
8e48c34
Filter restart sessions to applies
BenCodez Aug 30, 2026
b2af4c8
Fix recovery cleanup and restart history
BenCodez Aug 30, 2026
0ae5a4e
Discard stale dedicated setup reads
BenCodez Aug 30, 2026
1fd08c9
Reset setup controls after reconnect
BenCodez Aug 30, 2026
9767ff4
Harden durable recovery and restart state
BenCodez Aug 30, 2026
bbd3a88
Reclaim stale restart markers safely
BenCodez Aug 30, 2026
92bfb01
Reset dedicated setup on server changes
BenCodez Aug 30, 2026
5675d9a
Preserve unknown restart and UI state safety
BenCodez Aug 30, 2026
a005198
Reset routing state after reconnect
BenCodez Aug 30, 2026
cd9690b
Validate snapshot backups before recovery
BenCodez Aug 30, 2026
d51343e
Keep rollback recovery and accurate restart warnings
BenCodez Aug 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Maintainer and AI-agent guide

This repository is the standalone VotingPlugin Control service. Treat it as an optional management plane: it may inspect
and configure enrolled VotingPlugin nodes, but it must never become part of vote processing. VotingPlugin must continue to
start, accept votes, reward players, route proxy traffic, and stop normally when Control is absent or unavailable.

## Build and verification

Requirements: JDK 17+ and Maven 3.9+.

```shell
mvn -B clean verify
node --check src/main/resources/web/app.js
```

Use a focused Maven test while iterating, then run the complete command before opening a PR:

```shell
mvn -B -Dtest=InspectionOperationsTest test
mvn -B -Dtest=ControlHttpServerTest test
```

The CI definition is `.github/workflows/maven.yml`. The shaded runnable artifact is
`target/votingplugin-control-<version>-all.jar`.

## Architecture and file map

- `ControlApplication` parses owner commands/environment, creates durable stores, and wires the server.
- `http/ControlHttpServer` is the only HTTP boundary. It owns routing, authentication, CSRF enforcement, bounded JSON
parsing, status/error mapping, and static WebUI delivery.
- `auth/` stores credential verifiers and short-lived browser sessions. Never persist or log raw credentials.
- `domain/InMemoryNodeRegistry` owns current node sessions, liveness, negotiated capabilities, plugin inventory, and
topology. Current topology is deliberately in memory.
- `domain/ConfigurationOperations` coordinates live typed `READ`, `PREVIEW`, and `APPLY` tasks plus same-process retries.
Nodes pull tasks; Control never connects inbound to a Minecraft server.
- `domain/ConfigurationOperationJournal` stores redacted operation history across restarts. It never stores proposal values,
file contents, approval tokens, result messages/changes, credentials, or task attempts. Completed-result session IDs are
retained only to preserve restart-required UI state; the operations list exposes that state separately from its rendered
history window. Operation IDs must be unique, completed nodes must have a session ID, and incomplete nodes must omit it.
Recovered operations are history-only and cannot resume work; an interrupted node remains incomplete in the journal
across later shutdowns even though its recovered public result is `CONTROL_RESTARTED`. Persist automatic capability,
role, topology, and dependency cancellations at the state transition; do not wait for graceful shutdown.
Quick-setup history uses an internal, non-serialized `redacted` marker, and proposal validation must reject that marker.
- `domain/InspectionOperations` coordinates the separate, read-only `data.inspect.v1` lane.
- `domain/ConfigurationSnapshots` stores bounded copies of the redacted content returned by completed managed-file reads.
A successful empty file remains a non-null empty document rather than being confused with omitted content. The list API
omits content, full reads are admin-only, and durable files are owner-permissioned where the platform supports POSIX
modes. Restoring still uses the normal preview and one-time approval path.
- `domain/ConfigurationAuditLog` is the durable, bounded, hash-chained metadata audit log. Configuration values and query
filters do not belong there.
- `protocol/` contains the wire DTOs and capability-to-domain mapping. Keep them immutable and validate at construction or
at the HTTP boundary.
- `src/main/resources/web/` is a dependency-free browser client over the same `/api/v1` API.
For managed-file editing, content presence is separate from string length: a successful read or snapshot restore can
intentionally load `""` and must still enable preview. Clear that presence state whenever authentication, server, or
file context changes, and set it for direct editor input as well as non-null API content.
- `src/test/java/` mirrors the security and protocol boundaries. Add regression tests at the narrowest responsible layer.
- `docs/control-management.md` is the human and AI reference for the management suite and inspection contract.

## Non-negotiable invariants

1. Control is optional and local-first. Do not add a vote-processing dependency, cloud requirement, or inbound listener to
VotingPlugin.
2. Do not add arbitrary command execution, raw SQL, generic filesystem access, unrestricted configuration paths, or an
untyped player/database/settings browser. New functions must be a narrow typed capability. Quick-setup presets and
option names are fixed in Control and revalidated by the node for the requested phase; the WebUI settings catalog is a
static versioned reference, not an arbitrary setting-write surface.
3. Capability negotiation is authoritative. Queue work only for an online node whose accepted capabilities contain the
exact versioned capability. Unknown advertised capabilities remain unaccepted.
4. Configuration writes follow `READ`/`PREVIEW`/`APPLY`. Apply consumes the one-time approval from a completely successful
preview and carries the node revisions that were previewed. Do not create a shortcut around this workflow.
5. A claimed task has a two-minute lease and a unique `attemptId`. A result must echo the current node session, the exact
session that claimed the lease, and the attempt; stale attempts or post-claim reconnects cannot complete work. Claim,
completion, and capability-loss state transitions must roll back if their durable audit append fails.
6. The inspection lane is read-only. Its allow-listed kind and bounded string filters are the whole request; results are a
structured JSON envelope whose serialized size is at most 512 KiB, and are retained only briefly. Audit the kind, never
player names or other filter values.
7. Mask passwords, credentials, tokens, API keys, authorization values, webhook secrets, and comparable secrets before a
read leaves a node. Never put secrets, proposed file contents, approval tokens, or inspection filters in logs/audit.
Configuration snapshots persist only the node's redacted read result; do not weaken masking, admin authorization, or
data-directory permissions.
8. Browser writes require both an authenticated session and its CSRF token. API automation uses the separate admin bearer
credential; node endpoints use a credential bound to the exact node ID.
9. Treat all remote strings, collections, and bodies as hostile. Preserve limits, exact routes/methods, duplicate-field
rejection, required node-ID validation before registry lookup, symlink checks, atomic publication, and strict
fail-closed durable-file JSON validation (unknown/duplicate fields and trailing tokens are invalid).
10. Keep the HTTP executor, password executor, operation stores, retained messages/content, topology, and inspection data
bounded. Do not replace limits with unbounded queues, streams, maps, or full database scans.
11. `reward-simulation` and `reward-builder` share one strict proposal schema, but only the latter can persist. Keep the
builder PREVIEW/APPLY-only (Control must reject it on READ before queueing), replace only its selected Rewards subtree,
strip `proposal` from public/history views, and never execute reward actions from Control.
12. Keep discovered service names observational. `vote-site-health` may copy a bounded view of persisted
`GottenServiceSites`, but it must not call an auto-creating resolver or turn a health read into a create/approve action.
13. The current `vote-logging` quick setup changes configuration but not the runtime VoteLog manager lifecycle. Preserve
inspection gating on `VoteLogging.Enabled`, document that a restart is required after either toggle, and do not claim
enabled/available/readable are interchangeable states. Serialize APPLY operations and retries that share a target so
retained creation order is also successful completion order for restart-session warnings.

## Paired protocol workflow

The implementation paired with this repository lives in `BenCodez/VotingPlugin`:

- Bukkit configuration adapter: `VotingPlugin/.../control/BackendConfigurationService.java`
- Bukkit outbound connector: `VotingPlugin/.../control/BackendControlConnector.java`
- Bukkit inspection handlers: `VotingPlugin/.../control/ControlInspectionService.java`
- proxy connector/host lifecycle: `VotingPlugin/.../proxy/control/`
- paired contract: `docs/control-agent-contract.md` in that repository

When changing a DTO, endpoint, capability, preset, error code, limit, or lease behavior:

1. inspect both repositories before editing;
2. make the change additive or capability-versioned so either old side remains safe;
3. update server and connector protocol tests in their respective PRs;
4. update `docs/control-management.md`, the VotingPlugin connector docs, and both root `AGENTS.md` files when an invariant
changes;
5. link the two PRs and state a safe merge order. A Control-only deployment must reject unsupported work cleanly, and a
VotingPlugin-only deployment must simply leave the new capability unaccepted.

Prefer one cohesive PR per repository for a paired feature (implementation, tests, and docs together). Split further only
when the pieces are independently deployable or need materially different review/rollback risk.

Protocol version `1` describes the registration/heartbeat resource protocol. Feature evolution normally uses a new
capability such as `data.inspect.v1`; do not bump the whole protocol for an optional additive feature.

## Safe change checklist

- Identify the trust boundary and maximum sizes before adding the happy path.
- Validate exact methods, paths, media type, authentication role, session, capability, and request fields.
- Decide whether data may be persisted, audited, returned to the browser, or must be discarded on logout/restart.
- For configuration: prove preview/apply revision binding, approval single use, reload behavior, and rollback reporting.
- For inspection: prove the handler cannot write, enumerate users, accept SQL/commands/paths, or leak sensitive settings.
- Add negative tests for wrong node/session/attempt, lease expiry, unknown fields/kinds, oversized input/result, and lost
capability where relevant.
- For WebUI changes, escape untrusted text through DOM text nodes, clear sensitive/cached state and form inputs on logout,
reset dependent control state, keep CSRF on every write, and run `node --check`.
- Run the full Maven suite and inspect `git diff --check` before pushing.
- Keep the PR scoped; never mix generated artifacts, credentials, runtime `data/`, or unrelated formatting changes.

## Operational terminology

- A **configuration operation** may read, preview, or apply one typed configuration proposal.
- An **inspection** is a short-lived typed read and can never mutate a node.
- A **snapshot** is a durable Control-side copy of a successful redacted managed-file read result, not a raw server backup
and not an apply operation.
- A **vote trace** is a timeline of events that VotingPlugin actually wrote to VoteLog for one correlation ID. It is not a
packet-level or command-by-command delivery trace.
- A **diagnostics bundle** is the bounded, redacted inspection result assembled by the WebUI. It deliberately omits raw
configuration, logs, player records, credentials, and infrastructure connection details.
Loading
Loading