Chore/license split apache wire format#5
Closed
Daniel63656 wants to merge 40 commits into
Closed
Conversation
A Go CLI under go/cmd/fh-agent that turns a high-level site.spec.yaml
into a Docker Compose stack ready to scp + 'docker compose up' on a
Raspberry Pi 5, NVIDIA Jetson Orin Nano, x86 NUC, STM32MP25, or Bosch
Rexroth ctrlX CORE.
Pipeline: spec → plan → validate → build.
spec authoring & schema-export (read/write spec only)
targets introspection over 5 embedded hardware profiles
models suggest --target X --capability Y, RAM-filtered
plan deterministic compile of spec × target into 6 artifacts
(workflow, mapping, resources, manifest, local-models, meta)
validate cross-check the build dir (channels↔mapping↔resources↔manifest,
model RAM vs target)
build render Compose stack (engine + llama-server + optional
mosquitto) + README + .env.example, optional --tar
Designed for agents:
- JSON-first I/O, identical diagnostic shape across commands
- Documented exit codes (0/1/2/64)
- Deterministic plan (sorted keys, hashed node ids) so an iterating
agent sees only meaningful diffs
- 'targets describe' and 'spec schema' double as documentation so
Claude doesn't need to load 900 lines of contract YAML
Bundle uses the official ghcr.io engine image with configs mounted as
volumes — no per-customer Docker build, no AGPL-derivative-work question.
v1 limits documented in the README: no contract-schema validation
(separately run fh-workflow validate), no provisioning/OTA/HIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fh-agent validate now shells out to `fh-builder validate <wf> --json`
to catch contract violations the Go-side cross-checks can't see (missing
required params, wrong arg names, expression shape). Diagnostics merge
into fh-agent's existing JSON array — same shape, same exit codes.
Resilient: if fh-builder is not in PATH (e.g. on a customer device
without Node), validate emits one warn diagnostic and continues.
`--skip-workflow-check` silences it explicitly.
Required TS-side change:
- ts/app/cli/validate.ts: new `--json` flag emits a flat diagnostics
array on stdout (was human text only). Same field names as
fh-agent's Diagnostic.
Plan fixes uncovered by the new check:
- Ticker: emit intervalValue + intervalUnit, not legacy intervalMs
- ReadPin/WritePin/OnPinEdge: use `pinReference`, not `channelReference`
- agentTask edges: now carry the required `prompt` Expression
- Expression literals: non-empty default per dataType ("0"/"false"/"")
Example bundle (muellers-haus) now passes both Go cross-checks AND
fh-builder contract validation end-to-end.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Scoped roadmap for go/cmd/fh-agent/ — principles, shipped versions (v1.0 + v1.1), v1.x hardening backlog ordered by value/effort, v2 phase candidates, and explicit out-of-scope items. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The entire file was commented out with only the package declaration active. No code referenced RetryWithResilienceContext anywhere in the tree. Drop the dead stub. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Configure weekly Dependabot updates for the Go module under /go, the npm workspace under /ts, and GitHub Actions workflows. Minor and patch updates are grouped per ecosystem to reduce PR noise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Enable GitHub Sponsors button for the ForestHubAI org. Other funding platforms are listed commented-out as a reference for later. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Route reviews per subtree to @ForestHubAI/maintainers: - /go/, /ts/ — language subtrees - /contract/ — critical: drift between go/ts implementations - /.github/ — CI, workflows, repo meta - * — default fallback Note: the @ForestHubAI/maintainers team must be created in GitHub org settings (Org Settings → Teams) for the mentions to resolve. Until then GitHub will show "Unknown owner" warnings on the branch-protection UI but rules silently no-op. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pin per-language indentation and whitespace defaults so editors without project-aware tooling agree with gofmt, Prettier, and YAML conventions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The auth middleware compared the configured shared secret against the incoming Authorization header with `!=`, which short-circuits on the first differing byte. A network-adjacent attacker could in principle use response-time differences to learn the secret prefix-by-prefix. Switch to `crypto/subtle.ConstantTimeCompare` after a length check (length is not secret information, so an early length-mismatch return does not weaken the guarantee). Add table-style tests covering the empty-secret, missing-header, wrong-token and correct-token cases. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Keep-a-Changelog 1.1.0 format. Backfills go/v1.0.1 and ts/0.1.1 release lines from git history; opens an Unreleased section covering the fh-agent CLI, dependabot, CODEOWNERS, and README rewrite work landed since the tag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lints contract/*.yaml on PR + push to main with stoplightio/spectral-action. Baseline ruleset extends spectral:oas; first run surfaces 12 errors (mostly no-$ref-siblings + missing paths on llmproxy/workflow) and 31 warnings. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The shared-secret bearer middleware ran on every operation including Healthz, breaking the standard container-orchestrator pattern where a kubelet, Docker Compose healthcheck or ECS agent probes /healthz unauthenticated. The probe would 401 and the orchestrator would mark the engine unhealthy or refuse to route traffic to it. Short-circuit the middleware when the strict-handler operationID is "Healthz" so the probe reaches the handler without credentials. The response only reports "is the runner attached" — no workflow content or node state — so the exemption is safe to disclose. Other operations (Deploy, Stop) continue to require the bearer. Tests cover Healthz bypass with both a configured and an empty secret. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The vendored backend httpclient used &http.Client{} with no timeout.
Per-call context.WithTimeout is used on the boot/heartbeat paths
(BootCallbackTimeout=10s, HeartbeatTimeout=5s, ProviderLoadTimeout=10s),
but Engine.Deploy → memory.Restore → Snapshot inherits an uncapped ctx
from context.WithCancel(Background) and would hang on an unreachable
backend. RAG/LLM-chat call sites from runner nodes are likewise uncapped.
A 30s defaultTimeout on the http.Client gives defense-in-depth without
interfering with the tighter per-call timeouts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds .github/workflows/lint.yml with two parallel jobs: - go-lint: golangci/golangci-lint-action@v6 against go/, Go version from go.mod - ts-lint: npm ci && npm run lint in ts/ (eslint flat config already present) Separate from ci.yml to keep lint-failures non-blocking for test runs and to isolate concerns. permissions: contents: read only. Also adds go/.golangci.yml with sensible defaults: - enabled: govet, errcheck, staticcheck, ineffassign, unused, gosimple, gosec - gosec noisy rules excluded (G104, G304, G404) - govet: enable-all minus fieldalignment + shadow - test files exempt from gosec/errcheck - generated go/api/ tree excluded Local verification: - ts lint runs clean (0 findings) via `npm run lint` - golangci-lint not installed locally; YAML syntax verified for both files - Go lint findings: unknown until first CI run (follow-up: fix or relax) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Disables blank issues and routes off-topic intents (questions, security reports, commercial license inquiries) to Discussions, private security advisories, and mailto:root@foresthub.ai respectively. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…gistry Removes the GitHub Packages registry pin (which forced consumers to set up an auth token even for read access) and points the package at the public npm registry instead. Adds publishConfig.access=public so npm treats the scoped @foresthubai/* package as openly published on first publish. Tightens the files whitelist to dist + README + LICENSE; the previous list included src, which double-shipped TypeScript sources alongside the compiled dist/ output (npm pack now reports ~87 kB / 265 files for the tarball). Follow-up: no release workflow exists today, but when one is added it must not pin registry-url to https://npm.pkg.github.com. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… registry Mirrors the workflow-core switch: drops the GitHub Packages registry pin so consumers can install without an auth token, and sets access=public so the scoped package is published openly. Tightens the files whitelist: dist + tailwind-preset.ts + README + LICENSE plus src/styles/ (load-bearing — the exports map points "./styles/index.css" at "./src/styles/index.css", so consumers' CSS import would break if the whole src/ tree were excluded). The rest of src/ (TS sources) no longer ships. Tarball is now ~212 kB / 394 files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…nodes Hero now leads with a concrete, verified number (~32 MB compressed image, measured by cross-compiling cmd/engine for linux/arm64 and linux/amd64 with CGO_ENABLED=0 and -ldflags='-s -w' on top of distroless/static). Replaces the previous ~15 MB figure which was not reproducible from a fresh build. Drops OPC-UA from the pitch — it appears only as a ctrlX-OS pass-through note in a target manifest, not as an engine node (go/engine/driver and go/engine/transport implement GPIO/ADC/DAC/PWM, UART/serial, and MQTT). Pi Zero 2W is replaced by generic "Raspberry Pi" wording since no Pi Zero target is in cmd/fh-agent/targets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Publishes ghcr.io/foresthubai/edge-agents/engine on every `go/vX.Y.Z` tag — the same tag that releases the Go module — so the README's `docker run ...` quickstart actually resolves once the next tag is pushed. - linux/amd64 + linux/arm64 via buildx + QEMU (cross-compile in builder stage, only the final COPY into distroless is emulated) - docker/metadata-action strips the `go/` prefix so the image gets clean semver tags (1.2.3, 1.2, 1) plus `latest` - keyless cosign signing via GitHub OIDC, signs each tag-by-digest - SLSA build provenance + SBOM attestations pushed to the registry - workflow_dispatch with optional tag override for one-off rebuilds Triggered exclusively on `go/v*` tag-push and manual dispatch; no rolling build on main, to keep `:latest` aligned with a real release tag. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds four shields.io badges that work today: Go version (resolved from go/go.mod, currently 1.25), GitHub stars, last commit, and open issues. Each URL was probed (HTTP 200, real SVG content) before being added. Skipped: release badge (no GitHub releases published yet — only the go/v1.0.1 git tag exists), Docker Pulls (no public image), Discord (no server), Codecov (not wired). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the publishConfig switch on both TS packages: replaces the GitHub Packages walkthrough (consumer .npmrc + FH_PACKAGES_TOKEN) with the npmjs.org flow (npm login on the publish machine, no consumer setup required). Drops the "going public" footnote since that is now the steady state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Runs the GitHub Advanced Security CodeQL analyzer on push to main, on every PR to main, and on a weekly Sunday schedule (catches CVEs disclosed against already-merged code). Matrix covers go (autobuild via go/go.mod) and javascript-typescript (the modern combined identifier; supersedes the split javascript + typescript languages). security-and-quality query pack runs the security suite plus the quality rules — slightly noisier than security-only but worth it for a public repo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New section frames edge-agents against three projects users frequently evaluate alongside it (LangGraph, n8n, Dify). Comparison axes are container size, offline mode, hardware I/O, MQTT, visual builder, local SLMs, and license. Container sizes are sourced: n8n ~368 MB and Dify ~900 MB from current Docker Hub tags; edge-agents ~32 MB from the same cross-compile + distroless/static measurement used in the hero. OpenClaw was considered but dropped — no authoritative size data and the project is not comparable in scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Runs gitleaks-action on every push and PR, with fetch-depth: 0 so the scan covers full git history (not just the latest diff) — historic leaks block the merge just like fresh ones. No GITLEAKS_LICENSE configured: the paid tier is unnecessary for a public repo. If the default ruleset throws false positives on third-party notices, generated code, or vendored snippets we'll add a .gitleaks.toml allowlist in a follow-up commit — the first real CI run on this branch will surface what (if anything) needs ignoring. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…N-Schema) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…g block Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The contract/ directory defines the language-neutral wire format (OpenAPI 3.0.3 schemas) for the edge-agents platform. Splitting its license to Apache-2.0 allows third-party clients, SDKs, and tooling to implement the wire format without inheriting the AGPL terms that apply to the engine and builder. Verbatim text from https://www.apache.org/licenses/LICENSE-2.0.txt (sha256 cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Clarifies that contract/ is licensed under Apache-2.0 while the surrounding repository (engine, llmproxy, ts/workflow-builder, app) remains AGPL-3.0 / commercial. The NOTICE matches the wording style of the top-level NOTICE for consistency. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…kage The workflow-core package is the headless wire-format binding consumers embed in their own tooling, so it carries Apache-2.0 separately from the repository-level AGPL-3.0 license that covers the engine and the React workflow builder. LICENSE is the verbatim Apache-2.0 text; NOTICE points readers back to the repository LICENSE for the AGPL terms covering the rest of the tree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sets the npm "license" field to Apache-2.0 and adds NOTICE to the publishable "files" allowlist so the Apache-2.0 NOTICE ships in the tarball alongside LICENSE. The package itself stays self-describing on the npm registry without the repository-level AGPL terms leaking into downstream consumers' license scans. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a short README so the package has a landing page on npm covering what it is (headless workflow model + validator, no React, no DOM), how to install and validate a workflow, the three-layer architecture, and the Apache-2.0 / repo AGPL-3.0 license split. Linked from the "files" allowlist already shipping in the tarball. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the AGPL-only dual-license paragraph with a component-by- component table that maps contract/ and ts/workflow-core to Apache-2.0 and keeps engine, workflow-builder, and app under AGPL-3.0/commercial. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Top-level NOTICE now explicitly states the two-tier license split and points to contract/LICENSE/NOTICE and ts/workflow-core/LICENSE/NOTICE for the Apache-licensed parts, while keeping the AGPL/commercial wording scoped to the remaining components. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Updates the License and Contributor Agreement intro to name the two tiers explicitly, and adds a closing "Which license your contribution falls under" subsection so contributors know whether a PR ships under Apache-2.0 or AGPL-3.0-only based on the path it touches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ckage The workflow-builder package.json files-whitelist already references LICENSE but the file was missing, which would publish the package to npm with no license file in the tarball. Add the full AGPL-3.0 text (copied verbatim from the top-level LICENSE) and a workflow-builder-specific NOTICE that mirrors the wording used by workflow-core/NOTICE and clarifies that this package is AGPL while its workflow-core dependency is Apache-2.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…e.json Without an explicit license field, npmjs.com displays "no license" on the package page even though LICENSE ships in the tarball. Set the SPDX identifier explicitly and add NOTICE to the files whitelist so attribution ships alongside the license text (matching workflow-core's pattern). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The intro claimed edge-agents was AGPL-only, which is stale after the license split. Restate the model (Apache-2.0 for contract/ and workflow-core/, AGPL-3.0-only or commercial for the rest) and clarify that the listed third-party licenses are compatible with both tiers. The component list itself is untouched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CLA checkbox only mentioned AGPL-3.0 relicensing, which made the template misleading for PRs that touch only contract/ or workflow-core/ (both Apache-2.0). Restate both tiers so contributors agree to the license applicable to the paths they actually touched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Related Issues
Type of Change
Checklist
go vet ./...,go build ./..., andgo test ./...pass (ifgo/is touched)npm run typecheck,npm run lint,npm run build, andnpm testpass (ifts/is touched)contract/, I regenerated both sides (go generate ./...andnpm run generate) and committed the result*.gen.go,workflow-core/src/api/workflow.ts)Contributor License Agreement