feat(harness): P1+P2+P3 harness optimization complete (VERSION 1.4.0)
Task 5 one-line registry descriptions on the 8 biggest skills (remediation-tool,
gc-audit, packetdial, memory-dream, human-flow, self-check, impeccable,
mailprotector); skill-description injection ~3320 -> ~2123 tokens (~36%),
keyword triggers preserved, frontmatter valid.
Task 7 thinned /save + /sync bodies to point at sync.sh (single source) instead of
re-documenting internals; Phase 0 save-vs-sync, cross-user notes, exit-75
reporting kept verbatim; mechanical sync never depends on an LLM step.
Task 10 session-logs/YYYY-MM/ forward convention for new logs (scoped-grep recall,
no monolithic index); existing flat logs untouched (grep covers both).
Bash now-phoenix.sh helper (fixed UTC-7 epoch math; replaces unreliable
TZ=America/Phoenix date that silently returns UTC on Git-Bash).
P0 (1.2.0) + Task 6 CLAUDE split + Task 9 delegation (1.3.0) already shipped.
Spec: specs/claudetools-harness-optimization/plan.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,166 +1,156 @@
|
||||
---
|
||||
name: mailprotector
|
||||
description: >-
|
||||
Manage the Arizona Computer Guru (ACG) Mailprotector CloudFilter email-security
|
||||
gateway via the live CloudFilter REST API (emailservice.io). Search and release
|
||||
held / quarantined mail (inbound and outbound), pull mail-flow logs to explain
|
||||
why a message did or did not deliver, inspect entity configuration and
|
||||
allow/block rules, find a user or alias by email, and manage allow/block rules.
|
||||
Read-only by default; every release / rule-add / config-change is gated behind
|
||||
--confirm. Invoke for: "mailprotector", "cloudfilter", "emailservice.io", "held
|
||||
mail", "quarantined email", "release email", "outbound quarantine", "why didn't
|
||||
my email arrive", "email security gateway", "INKY", "mail flow logs", "allow
|
||||
block rule", "release spam". This skill talks to the LIVE production reseller
|
||||
CloudFilter platform — treat releases conservatively.
|
||||
---
|
||||
|
||||
# Mailprotector / CloudFilter Skill
|
||||
|
||||
Standalone CLI client for the **Mailprotector CloudFilter REST API**
|
||||
(`emailservice.io`), the reseller email-security platform ACG layers on top of
|
||||
client mail flow. Read-only by default; every write (release, rule add, config
|
||||
change) is gated behind `--confirm`.
|
||||
|
||||
## The two-layer context (important)
|
||||
|
||||
ACG's email security sits in front of client mailboxes as two cooperating layers:
|
||||
|
||||
| Layer | What it does |
|
||||
|---|---|
|
||||
| **Mailprotector CloudFilter** | The delivery / filtering gateway. Inbound and outbound mail passes through it; spam, virus, and policy hits are **held / quarantined** here. Releasing a held message re-injects it for delivery. This is the API this skill drives. |
|
||||
| **INKY** | Email annotation / phishing-banner layer. Adds the warning banners and protects against impersonation. Not part of this API surface. |
|
||||
|
||||
Both sit **layered on top of the client's own Exchange / M365 mail flow** — so a
|
||||
"missing email" investigation usually means: was it held at CloudFilter (check
|
||||
`messages` / `logs`), or did it pass CloudFilter and stall in Exchange?
|
||||
|
||||
## Connection
|
||||
|
||||
| Item | Value |
|
||||
|---|---|
|
||||
| Base URL | `https://emailservice.io/api/v1` (override `MAILPROTECTOR_API_BASE_URL`) |
|
||||
| Auth | `Authorization: Bearer <api_key>` |
|
||||
| Vault entry | `msp-tools/mailprotector.sops.yaml`, field `credentials.api_key` |
|
||||
| Env override | `MAILPROTECTOR_API_KEY` |
|
||||
|
||||
Credential resolution order: `MAILPROTECTOR_API_KEY` env -> vault
|
||||
`credentials.api_key`. The key is never hardcoded; a clear setup error is raised
|
||||
if neither resolves.
|
||||
|
||||
### Scopes
|
||||
|
||||
Five entity types carry `logs` / `messages` / `configuration` /
|
||||
`allow_block_rules` / `users` / `domains` sub-resources. Path form is
|
||||
`/{scope}/{id}/...`:
|
||||
|
||||
```
|
||||
resellers, customers, domains, user_groups, users
|
||||
```
|
||||
|
||||
The CLI validates `scope` against this set.
|
||||
|
||||
## Running the CLI
|
||||
|
||||
This machine's Python launcher is `py` (per identity.json); `python` / `python3`
|
||||
also work. Run from the scripts dir so the two modules resolve.
|
||||
|
||||
```bash
|
||||
cd C:/claudetools/.claude/skills/mailprotector/scripts
|
||||
|
||||
py mp.py status # validate token (GET /domains, per_page=1)
|
||||
py mp.py domains # list domains (global)
|
||||
py mp.py domains --scope customers --id <id>
|
||||
py mp.py domain <domain_id>
|
||||
py mp.py customers <reseller_id>
|
||||
py mp.py customer <customer_id>
|
||||
py mp.py users <scope> <id>
|
||||
py mp.py user <user_id>
|
||||
py mp.py find-user user@client.com # locate a user / alias by email (a READ)
|
||||
py mp.py config <scope> <id> # shows permissions.messages.allow_spam_release
|
||||
py mp.py rules <scope> <id>
|
||||
```
|
||||
|
||||
### Mail-flow logs and held mail (the common investigation)
|
||||
|
||||
Both accept the same filters: `--sender --recipient --subject --decision
|
||||
--sort-field --sort-direction --page --page-size`.
|
||||
|
||||
```bash
|
||||
# Why didn't this arrive? Look at the decision in the flow logs.
|
||||
py mp.py logs domains <domain_id> --recipient ceo@client.com --decision quarantine_spam
|
||||
|
||||
# Held / quarantined mail search.
|
||||
py mp.py messages domains <domain_id> --sender boss@vendor.com
|
||||
```
|
||||
|
||||
`--decision` values: `default`, `deliver`, `quarantine_spam`,
|
||||
`quarantine_virus`, `quarantine_policy`, `bounce`, `encrypt`, `delete`.
|
||||
`--sort-field` values: `@timestamp` (default), `prime.direction`,
|
||||
`prime.from_header_raw`, `prime.recipient`, `prime.subject`, `prime.decision`,
|
||||
`prime.score`.
|
||||
|
||||
## Writes (gated)
|
||||
|
||||
Every mutating command prints a `[DRY RUN]` line and exits non-zero unless you
|
||||
pass `--confirm`.
|
||||
|
||||
```bash
|
||||
py mp.py release <message_id> --confirm
|
||||
py mp.py release <message_id> --recipients alt@client.com --confirm
|
||||
py mp.py release-many <scope> <id> --ids 111,222,333 --confirm
|
||||
py mp.py release-many <scope> <id> --all --confirm
|
||||
py mp.py add-rule <scope> <id> --value vendor.com --type allow --confirm
|
||||
py mp.py enable-release <scope> <id> --confirm
|
||||
```
|
||||
|
||||
## The `allow_spam_release` gotcha
|
||||
|
||||
Releasing a held **spam** message fails if the owning entity does not have
|
||||
`permissions.messages.allow_spam_release = true`. Workflow:
|
||||
|
||||
1. `py mp.py config <scope> <id>` — check `allow_spam_release`.
|
||||
2. If `false`: `py mp.py enable-release <scope> <id> --confirm`.
|
||||
3. Re-run the `release` / `release-many`.
|
||||
|
||||
Virus and policy quarantines are governed separately — only spam release is
|
||||
gated by this permission.
|
||||
|
||||
## Example workflow: find a client's held outbound mail from a sender and release it
|
||||
|
||||
```bash
|
||||
# 1. Find the client's domain.
|
||||
py mp.py domains --scope customers --id <customer_id>
|
||||
|
||||
# 2. Search held messages from the sender (outbound = sender is the client user).
|
||||
py mp.py messages domains <domain_id> --sender user@client.com --decision quarantine_spam
|
||||
|
||||
# 3. If it's spam-held, make sure release is permitted on the domain.
|
||||
py mp.py config domains <domain_id> # check allow_spam_release
|
||||
py mp.py enable-release domains <domain_id> --confirm # only if needed
|
||||
|
||||
# 4. Release by message id (DRY RUN first — omit --confirm to preview).
|
||||
py mp.py release <message_id> # [DRY RUN]
|
||||
py mp.py release <message_id> --confirm # actually release
|
||||
```
|
||||
|
||||
## Raw escape hatch
|
||||
|
||||
The named commands cover the common surface; for anything else, hit the path
|
||||
directly. Non-GET methods still require `--confirm`.
|
||||
|
||||
```bash
|
||||
py mp.py raw GET domains/<id>/logs
|
||||
py mp.py raw POST messages/<id>/deliver --body '{"include_original_recipients":1}' --confirm
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- This is the **LIVE production reseller CloudFilter platform**. A release
|
||||
re-delivers real mail to real recipients, and an allow rule can let real spam
|
||||
or phishing through — confirm the target entity with a read command before any
|
||||
write, and prefer releasing specific message ids over `--all`.
|
||||
- Pagination: `page` (default 1) and `per_page` (default 25); reseller
|
||||
`messages` caps `per_page` at 50. The `X-Pagination` response header carries
|
||||
the page/total metadata.
|
||||
- Full endpoint catalog, filter tables, and the global `field[op]=value`
|
||||
operators live in `references/api.md`.
|
||||
---
|
||||
name: mailprotector
|
||||
description: "Manage the ACG Mailprotector CloudFilter email-security gateway (emailservice.io). Search/release held/quarantined mail (in+outbound), pull mail-flow logs (why a message did/did not deliver), inspect + manage allow/block rules. Read-only default; releases/rule-changes gated --confirm. Triggers: mailprotector, cloudfilter, held/quarantined mail, release email, allow/block rule, INKY. Live production."
|
||||
|
||||
---
|
||||
|
||||
# Mailprotector / CloudFilter Skill
|
||||
|
||||
Standalone CLI client for the **Mailprotector CloudFilter REST API**
|
||||
(`emailservice.io`), the reseller email-security platform ACG layers on top of
|
||||
client mail flow. Read-only by default; every write (release, rule add, config
|
||||
change) is gated behind `--confirm`.
|
||||
|
||||
## The two-layer context (important)
|
||||
|
||||
ACG's email security sits in front of client mailboxes as two cooperating layers:
|
||||
|
||||
| Layer | What it does |
|
||||
|---|---|
|
||||
| **Mailprotector CloudFilter** | The delivery / filtering gateway. Inbound and outbound mail passes through it; spam, virus, and policy hits are **held / quarantined** here. Releasing a held message re-injects it for delivery. This is the API this skill drives. |
|
||||
| **INKY** | Email annotation / phishing-banner layer. Adds the warning banners and protects against impersonation. Not part of this API surface. |
|
||||
|
||||
Both sit **layered on top of the client's own Exchange / M365 mail flow** — so a
|
||||
"missing email" investigation usually means: was it held at CloudFilter (check
|
||||
`messages` / `logs`), or did it pass CloudFilter and stall in Exchange?
|
||||
|
||||
## Connection
|
||||
|
||||
| Item | Value |
|
||||
|---|---|
|
||||
| Base URL | `https://emailservice.io/api/v1` (override `MAILPROTECTOR_API_BASE_URL`) |
|
||||
| Auth | `Authorization: Bearer <api_key>` |
|
||||
| Vault entry | `msp-tools/mailprotector.sops.yaml`, field `credentials.api_key` |
|
||||
| Env override | `MAILPROTECTOR_API_KEY` |
|
||||
|
||||
Credential resolution order: `MAILPROTECTOR_API_KEY` env -> vault
|
||||
`credentials.api_key`. The key is never hardcoded; a clear setup error is raised
|
||||
if neither resolves.
|
||||
|
||||
### Scopes
|
||||
|
||||
Five entity types carry `logs` / `messages` / `configuration` /
|
||||
`allow_block_rules` / `users` / `domains` sub-resources. Path form is
|
||||
`/{scope}/{id}/...`:
|
||||
|
||||
```
|
||||
resellers, customers, domains, user_groups, users
|
||||
```
|
||||
|
||||
The CLI validates `scope` against this set.
|
||||
|
||||
## Running the CLI
|
||||
|
||||
This machine's Python launcher is `py` (per identity.json); `python` / `python3`
|
||||
also work. Run from the scripts dir so the two modules resolve.
|
||||
|
||||
```bash
|
||||
cd C:/claudetools/.claude/skills/mailprotector/scripts
|
||||
|
||||
py mp.py status # validate token (GET /domains, per_page=1)
|
||||
py mp.py domains # list domains (global)
|
||||
py mp.py domains --scope customers --id <id>
|
||||
py mp.py domain <domain_id>
|
||||
py mp.py customers <reseller_id>
|
||||
py mp.py customer <customer_id>
|
||||
py mp.py users <scope> <id>
|
||||
py mp.py user <user_id>
|
||||
py mp.py find-user user@client.com # locate a user / alias by email (a READ)
|
||||
py mp.py config <scope> <id> # shows permissions.messages.allow_spam_release
|
||||
py mp.py rules <scope> <id>
|
||||
```
|
||||
|
||||
### Mail-flow logs and held mail (the common investigation)
|
||||
|
||||
Both accept the same filters: `--sender --recipient --subject --decision
|
||||
--sort-field --sort-direction --page --page-size`.
|
||||
|
||||
```bash
|
||||
# Why didn't this arrive? Look at the decision in the flow logs.
|
||||
py mp.py logs domains <domain_id> --recipient ceo@client.com --decision quarantine_spam
|
||||
|
||||
# Held / quarantined mail search.
|
||||
py mp.py messages domains <domain_id> --sender boss@vendor.com
|
||||
```
|
||||
|
||||
`--decision` values: `default`, `deliver`, `quarantine_spam`,
|
||||
`quarantine_virus`, `quarantine_policy`, `bounce`, `encrypt`, `delete`.
|
||||
`--sort-field` values: `@timestamp` (default), `prime.direction`,
|
||||
`prime.from_header_raw`, `prime.recipient`, `prime.subject`, `prime.decision`,
|
||||
`prime.score`.
|
||||
|
||||
## Writes (gated)
|
||||
|
||||
Every mutating command prints a `[DRY RUN]` line and exits non-zero unless you
|
||||
pass `--confirm`.
|
||||
|
||||
```bash
|
||||
py mp.py release <message_id> --confirm
|
||||
py mp.py release <message_id> --recipients alt@client.com --confirm
|
||||
py mp.py release-many <scope> <id> --ids 111,222,333 --confirm
|
||||
py mp.py release-many <scope> <id> --all --confirm
|
||||
py mp.py add-rule <scope> <id> --value vendor.com --type allow --confirm
|
||||
py mp.py enable-release <scope> <id> --confirm
|
||||
```
|
||||
|
||||
## The `allow_spam_release` gotcha
|
||||
|
||||
Releasing a held **spam** message fails if the owning entity does not have
|
||||
`permissions.messages.allow_spam_release = true`. Workflow:
|
||||
|
||||
1. `py mp.py config <scope> <id>` — check `allow_spam_release`.
|
||||
2. If `false`: `py mp.py enable-release <scope> <id> --confirm`.
|
||||
3. Re-run the `release` / `release-many`.
|
||||
|
||||
Virus and policy quarantines are governed separately — only spam release is
|
||||
gated by this permission.
|
||||
|
||||
## Example workflow: find a client's held outbound mail from a sender and release it
|
||||
|
||||
```bash
|
||||
# 1. Find the client's domain.
|
||||
py mp.py domains --scope customers --id <customer_id>
|
||||
|
||||
# 2. Search held messages from the sender (outbound = sender is the client user).
|
||||
py mp.py messages domains <domain_id> --sender user@client.com --decision quarantine_spam
|
||||
|
||||
# 3. If it's spam-held, make sure release is permitted on the domain.
|
||||
py mp.py config domains <domain_id> # check allow_spam_release
|
||||
py mp.py enable-release domains <domain_id> --confirm # only if needed
|
||||
|
||||
# 4. Release by message id (DRY RUN first — omit --confirm to preview).
|
||||
py mp.py release <message_id> # [DRY RUN]
|
||||
py mp.py release <message_id> --confirm # actually release
|
||||
```
|
||||
|
||||
## Raw escape hatch
|
||||
|
||||
The named commands cover the common surface; for anything else, hit the path
|
||||
directly. Non-GET methods still require `--confirm`.
|
||||
|
||||
```bash
|
||||
py mp.py raw GET domains/<id>/logs
|
||||
py mp.py raw POST messages/<id>/deliver --body '{"include_original_recipients":1}' --confirm
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- This is the **LIVE production reseller CloudFilter platform**. A release
|
||||
re-delivers real mail to real recipients, and an allow rule can let real spam
|
||||
or phishing through — confirm the target entity with a read command before any
|
||||
write, and prefer releasing specific message ids over `--all`.
|
||||
- Pagination: `page` (default 1) and `per_page` (default 25); reseller
|
||||
`messages` caps `per_page` at 50. The `X-Pagination` response header carries
|
||||
the page/total metadata.
|
||||
- Full endpoint catalog, filter tables, and the global `field[op]=value`
|
||||
operators live in `references/api.md`.
|
||||
|
||||
Reference in New Issue
Block a user