Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 19 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
.agent
.git
.github
.hypothesis
.pytest_cache
.ruff_cache
.terraform
.venv
agents
build
dist
docs
htmlcov
logs
tests
validation
*.egg-info
*.pyc
__pycache__
53 changes: 50 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,53 @@ jobs:
- name: Lint
run: uv run ruff check --no-cache .

optional-cloud-draft-artifacts:
# These checks keep the optional draft internally consistent. They do not
# establish that it has been deployed or validated on a real provider.
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false

- name: Set up Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
with:
terraform_version: "1.15.8"
terraform_wrapper: false

- name: Check draft Cloud Run Terraform syntax
run: |
terraform fmt -check -recursive deploy/gcp-cloud-run
terraform -chdir=deploy/gcp-cloud-run init -backend=false -input=false -lockfile=readonly
terraform -chdir=deploy/gcp-cloud-run validate

- name: Build draft remote container
run: docker build --file deploy/container/Dockerfile --tag recon-remote:ci .

- name: Smoke-test draft remote container locally
shell: bash
run: |
set -euo pipefail
token="$(openssl rand -base64 48 | tr -d '\n')"
docker run --detach --name recon-remote-ci --publish 18080:8080 \
--env RECON_REMOTE_BEARER_TOKEN="$token" recon-remote:ci
trap 'docker rm --force recon-remote-ci >/dev/null 2>&1 || true' EXIT
for attempt in $(seq 1 30); do
if curl --fail --silent http://127.0.0.1:18080/health >/dev/null; then
break
fi
if [ "$attempt" -eq 30 ]; then
docker logs recon-remote-ci
exit 1
fi
sleep 1
done
test "$(curl --silent --output /dev/null --write-out '%{http_code}' \
--request POST --header 'Content-Type: application/json' \
--data '{}' http://127.0.0.1:18080/mcp)" = "401"

typecheck:
runs-on: ubuntu-latest
timeout-minutes: 15
Expand Down Expand Up @@ -81,15 +128,15 @@ jobs:
run: uv run pyright

mcp-compatibility:
# Exercise the supported stable SDK and the exact v2 release candidate in
# Exercise the supported stable v1 SDK and the exact stable v2 SDK in
# isolated environments. Production remains constrained to stable v1; the
# candidate pin is a compatibility proof, not a published dependency.
# v2 pin is a compatibility proof, not a published dependency.
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
mcp-version: ["1.28.1", "2.0.0b1"]
mcp-version: ["1.28.1", "2.0.0"]
env:
UV_PYTHON: "3.11"
steps:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ jobs:
run: >-
uv run python scripts/check_mcp_compatibility.py
--sdk-version 1.28.1
--sdk-version 2.0.0b1
--sdk-version 2.0.0
--require-compatible 1.28.1
--require-compatible 2.0.0b1
--require-compatible 2.0.0

- name: Run complete local quality gate
# A manually pushed tag cannot bypass the deterministic gate that
Expand Down
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ build/
*.whl
*.tar.gz

# Optional infrastructure working state
**/.terraform/
*.tfstate
*.tfstate.*
*.tfplan
*.tfvars
*.tfvars.json
crash.log
crash.*.log

# Test / Coverage
.pytest_cache/
.hypothesis/
Expand Down
29 changes: 24 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ of one organization, owner, account, or deployed product.
It uses no credentials, no API keys, no paid feeds, and no active scanning. It
ships as a local Python package with a CLI, versioned JSON output, and a stdio
MCP server.
It is not a hosted service, scheduler, vulnerability scanner, company research
tool, or firmographic database.
The project does not operate a hosted service. Optional draft guidance is
available for operator-owned remote access, but it is never required for local
use. recon is not a scheduler, vulnerability scanner, company research tool,
or firmographic database.

> **Defensive use only.** Use recon for legitimate posture review, IT
> architecture review, vendor diligence, and defensive hardening. See
Expand Down Expand Up @@ -292,6 +294,17 @@ guidance, and troubleshooting live in
Per-client scaffolds live in
[agents/](https://github.com/blisspixel/recon/tree/main/agents).

## Optional Cloud Access

Local execution remains the default. For teams that want shared remote access,
the repository includes a draft authenticated container and Cloud Run Terraform
starting point. The framework is intended to be directionally useful, not a
validated production deployment. Operators own deployment, identity, data
handling, cost, and operations.

- [Optional cloud architecture and platform plan](https://github.com/blisspixel/recon/blob/main/docs/optional-cloud-deployment-plan.md)
- [Draft deployment framework](https://github.com/blisspixel/recon/tree/main/deploy)

## Limitations

The public channel has a ceiling:
Expand All @@ -315,6 +328,8 @@ before committing any validation artifact.
- [docs/README.md](https://github.com/blisspixel/recon/blob/main/docs/README.md): complete docs index.
- [docs/roadmap.md](https://github.com/blisspixel/recon/blob/main/docs/roadmap.md): current plan, invariants, and scope
boundaries.
- [docs/optional-cloud-deployment-plan.md](https://github.com/blisspixel/recon/blob/main/docs/optional-cloud-deployment-plan.md): optional cloud
architecture, maturity, and validation gates.
- [docs/structural-maintainability.md](https://github.com/blisspixel/recon/blob/main/docs/structural-maintainability.md): measured
source, test, compatibility, and facade cleanup plan.
- [docs/external-writeup-plan.md](https://github.com/blisspixel/recon/blob/main/docs/external-writeup-plan.md): active
Expand All @@ -333,13 +348,17 @@ priorities are:
1. Make every default claim traceable to evidence and remove product-use,
cloud-type, or security-maturity conclusions that public metadata cannot
support.
2. Keep the exact MCP v1.28.1 and v2.0.0b1 compatibility matrix green, then
repeat the full gate against the final 2026-07-28 specification and stable
v2 SDK before changing the production dependency.
2. Keep the exact MCP v1.28.1 and v2.0.0 compatibility matrix green. The
stable-v2 compatibility gate passed on 2026-07-28; changing the production
dependency remains a separate, deliberate release decision.
3. Establish an aggregate-safe quality baseline for claim precision,
abstention, provenance, catalog coverage, degradation, latency, CT value,
and agent context cost before expanding inference or graph machinery.

A fourth, explicitly lower-priority track covers the optional cloud framework.
It does not change the local default and remains subject to the maturity and
validation gates in the linked plan.

Catalog coverage work uses deduplicated private rounds across rank, region, and
domain-class strata. Real target names and per-domain records stay in ignored
local validation workspaces. GitHub receives only generic provider patterns,
Expand Down
66 changes: 47 additions & 19 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ contract, the local stdio MCP server, bounded public-metadata collectors,
generated-artifact guards, the validation gates, and a release path with
reproducible builds, provenance, SBOM, and cross-channel byte parity.

An optional authenticated remote container and draft Google Cloud Run IaC
framework now exist as low-priority accessibility and scale polish. This draft
is intended to be directionally useful, not a validated production deployment.
It does not change the local default, and the project does not operate a hosted
endpoint.

Release verification binds every published artifact to its exact tag, workflow,
signer, and commit digest, and requires SBOM provenance. One
digest-bound v2.6.3 historical exception preserves that release's published
Expand All @@ -23,8 +29,8 @@ A complete baseline is not a finished product. Three things remain unproven,
and the plan below is about exactly those three:

- Not every default claim has been traced to the evidence that supports it.
- The MCP protocol recon speaks is about to change, and the final gate has not
run.
- Stable MCP v2 compatibility is now characterized, but production adoption
remains a separate release decision.
- Nothing measures whether probabilistic fusion, certificate-transparency
enrichment, the fingerprint catalog, or the broad agent surface improves an
operator outcome over deterministic evidence plus explicit abstention.
Expand Down Expand Up @@ -64,27 +70,24 @@ ownership, control, or current-use claims; missing metadata stays unknown; and
explanation output reports provenance completeness rather than asserting a
complete path it does not have.

### 2. Characterize the final MCP protocol before adopting it
### 2. Keep final MCP v2 compatibility green before adopting it

**Why now, despite ranking second:** this is the only track with an external
clock. The Model Context Protocol 2026-07-28 specification is a breaking
protocol release, and the official Python SDK moves on its own schedule
regardless of recon. Every other track moves at the maintainer's pace. This one
does not, and deferring it makes it harder rather than cheaper. The work itself
is bounded, which is why it can be scheduled without stalling track 1.
**Why second:** the Model Context Protocol 2026-07-28 specification is a
breaking protocol release, and the official Python SDK moves on its own
schedule regardless of recon. The compatibility work is bounded and remains
blocking in CI without displacing track 1.

**State:** the exact `1.28.1` and `2.0.0b1` matrix passed on 2026-07-13, and CI
keeps both pins blocking. The SDK published `2.0.0b2` on 2026-07-14, one day
after that run, so the characterized candidate is one release behind the
current beta.
**State:** the exact `1.28.1` and stable `2.0.0` matrix passed on 2026-07-28,
and CI keeps both pins blocking. The same registration and domain logic passes
legacy initialization and final stateless `server/discover` behavior.

**Closed when:** the dated matrix covers the current candidate and then the
final specification with the stable v2 SDK; tool and resource order stays
**Closed when:** the stable matrix stays green; tool and resource order stays
deterministic; declared output schemas and structured results conform on both
generations; and the local stdio workflow is intact. Production stays on
`mcp>=1.28.1,<2` until that full gate passes. Remote HTTP, OAuth, Roots,
Sampling, Apps, and Tasks are not adopted along the way without a named product
need and a separate architecture review.
generations; and the local stdio workflow remains intact. Production stays on
`mcp>=1.28.1,<2` until a separate adoption review changes it. The named
optional remote-access need and its separate architecture review now live in
[the cloud deployment plan](docs/optional-cloud-deployment-plan.md); that work
does not imply production v2 adoption, OAuth, Roots, Sampling, Apps, or Tasks.

### 3. Freeze a product-quality baseline, then promote or retire

Expand All @@ -101,6 +104,28 @@ whether advanced fusion stays in the primary path or becomes an explicitly
advanced diagnostic. An inconclusive or negative result is a valid outcome and
is not reinterpreted into a promotion.

### 4. Optional operator-hosted access and scale-out

**Why fourth:** making recon easier to reach from different AI systems is useful
polish for some operators, but it does not outrank output truthfulness,
protocol compatibility, or evidence that the product improves an operator
decision.

**State:** draft shared runtime, non-root OCI container, Cloud Run Terraform,
authentication boundaries, and CI structural checks exist. They pass local
artifact checks but are not yet provider-validated or production-ready. Local
CLI and stdio MCP remain the complete default. AWS AgentCore, Azure Container
Apps, Cloudflare, Kubernetes, and per-user OAuth are research directions with
explicit stop rules rather than unvalidated placeholder IaC.

**Closed when:** one external operator has validated the chosen reference with
a real MCP client, bounded load and cost evidence, credential rotation, log
retention, and image rollback. Expansion to another provider requires named
demand and that provider's validation context.

Full architecture, research, provider choices, and sequencing:
[docs/optional-cloud-deployment-plan.md](docs/optional-cloud-deployment-plan.md).

## What Is Deliberately Not Next

Each of these is real work that is blocked on purpose, not forgotten.
Expand All @@ -110,6 +135,8 @@ Each of these is real work that is blocked on purpose, not forgotten.
| Broad catalog growth | The independent rank, regional, vendor-seed, and drift rounds. A repeated list is a drift round, not new coverage. |
| More graph or probabilistic machinery | Measured benefit to a named user outcome, from track 3. |
| A core-versus-advanced MCP tool profile | A representative client proving material context benefit. Payload size alone is not the trigger. |
| More optional cloud provider IaC | A named operator, provider-specific identity and region context, and the acceptance gate in the optional cloud plan. |
| A project-operated public or multi-tenant service | A separate product, governance, privacy, abuse, support, and funding decision. The current plan provides operator-owned references only. |
| Promoting generated discovery artifacts to a stable contract | A named external consumer, under [ADR-0007](docs/adr/0007-surface-inventory-discovery-context.md). |
| Native acceleration in Rust, Go, or Mojo | The evidence gates in [ADR-0010](docs/adr/0010-evidence-gated-native-acceleration.md), measured on a real stage rather than a microbenchmark. |
| Dimensioned email posture scoring | An ADR plus the RFC 9989 completion audit, keeping the current stable field as a compatibility view. |
Expand Down Expand Up @@ -184,6 +211,7 @@ and the most recent completed historical local submission-freeze proof is
| How the next tracks get implemented | [docs/engineering-refinement-plan.md](docs/engineering-refinement-plan.md) |
| Source, test, and facade cleanup | [docs/structural-maintainability.md](docs/structural-maintainability.md) |
| MCP timeline, gate, and rollback criteria | [docs/mcp-2026-07-28-readiness.md](docs/mcp-2026-07-28-readiness.md) and [ADR-0009](docs/adr/0009-mcp-2026-readiness.md) |
| Optional remote MCP, cloud hosting, authentication, and scale-out | [docs/optional-cloud-deployment-plan.md](docs/optional-cloud-deployment-plan.md) |
| Catalog rounds and the promotion gate | [docs/catalog-strategy.md](docs/catalog-strategy.md) |
| The publication freeze gate | [docs/submission-freeze-checklist.md](docs/submission-freeze-checklist.md) |
| Earlier plans and superseded framing | [docs/roadmap-history.md](docs/roadmap-history.md) |
42 changes: 42 additions & 0 deletions deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Draft Optional Deployment Framework

These files are optional operator-owned deployment references. They are not
required to install or use recon, and the recon project does not deploy or
operate a hosted service. The default remains the local CLI and local stdio MCP
server.

Status: draft framework intended to be directionally useful. These files pass
repository syntax, build, and local protocol checks, but they are not yet
provider-validated or production-ready. They are starting points for evaluation
in an operator-owned non-production account, not a support promise or a claim
that the documented cloud behavior has been exercised end to end.

Available draft artifacts:

| Path | Purpose | Status |
|---|---|---|
| [container](container/README.md) | Authenticated, stateless remote MCP container | Draft, locally built and smoke-tested |
| [gcp-cloud-run](gcp-cloud-run/README.md) | Scale-to-zero Cloud Run service with Terraform | Draft, syntax-checked but not applied to Cloud Run |

AWS, Azure, Cloudflare, Kubernetes, Anthropic, OpenAI, and other client paths
are evaluated in the
[optional cloud deployment plan](../docs/optional-cloud-deployment-plan.md).
They are not represented here by placeholder infrastructure that has not met
its platform-specific validation gate.

Every operator owns the cloud account, identity provider, bill, logs, policy,
target allowlist if one is needed, upgrades, and incident response for their
deployment. No deployment sends telemetry or usage data to the recon project.

## How to evaluate the draft

1. Start with the cross-platform plan and choose the client, compute provider,
identity boundary, region, and expected traffic separately.
2. Use a dedicated non-production account or project with a budget and a hard
resource ceiling.
3. Build from a reviewed revision, pin the image digest and secret version, and
inspect the complete IaC plan before applying it.
4. Run the provider promotion checklist, including negative authentication,
load and cost bounds, log review, secret rotation, rollback, and deletion.
5. Call it provider-validated only after recording that evidence. Until then,
describe it as a draft framework and keep local recon as the default.
40 changes: 40 additions & 0 deletions deploy/container/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
ARG PYTHON_IMAGE=python:3.11.15-slim-bookworm@sha256:b18992999dbe963a45a8a4da40ac2b1975be1a776d939d098c647482bcad5cba

FROM ${PYTHON_IMAGE} AS builder

COPY --from=ghcr.io/astral-sh/uv:0.11.17@sha256:03bdc89bb9798628846e60c3a9ad19006c8c3c724ccd2985a33145c039a0577b /uv /uvx /bin/

WORKDIR /app
ENV UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy

COPY pyproject.toml uv.lock README.md LICENSE ./
COPY src ./src

RUN uv sync --frozen --no-dev --no-editable

FROM ${PYTHON_IMAGE} AS runtime

LABEL org.opencontainers.image.source="https://github.com/blisspixel/recon" \
org.opencontainers.image.licenses="Apache-2.0"

RUN useradd --create-home --uid 10001 --shell /usr/sbin/nologin recon \
&& mkdir -p /home/recon/.recon \
&& chown -R 10001:10001 /home/recon

WORKDIR /app
COPY --from=builder /app/.venv /app/.venv

ENV PATH="/app/.venv/bin:${PATH}" \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
RECON_REMOTE_HOST=0.0.0.0 \
RECON_REMOTE_PORT=8080

USER 10001:10001
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD python -c "import os, urllib.request; urllib.request.urlopen('http://127.0.0.1:' + os.environ.get('RECON_REMOTE_PORT', '8080') + '/health', timeout=2).read()"

ENTRYPOINT ["python", "-m", "recon_tool.remote_server"]
Loading
Loading