Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
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
14 changes: 14 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.git
.env
.env.*
.gstack
.claude
.codex
node_modules
browse/dist
design/dist
coverage
dist
tmp
*.log
.DS_Store
44 changes: 40 additions & 4 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
# Copy to .env and fill in values
# bun auto-loads .env — no dotenv needed
# Copy to .env and fill in values.
# This file is the primary local auth source for make/docker workflows.
# Nothing here is baked into images.
# Do not commit or plain-git sync a filled-in .env file.
# If you need cross-machine distribution, use an encrypted secret manager,
# encrypted git workflow, Syncthing, or another local-only transport.
#
# If .env is absent, cloud runners can still inject these variables via their
# own harness or secret manager and the Makefile will pass them through at runtime.
#
# For local interactive use, account login state from ~/.claude and ~/.codex is
# mounted into the container by default, so users with existing Claude Code or
# Codex logins usually do not need API keys for account-backed flows.
#
# General rule for other services:
# - If the service CLI stores login state on disk, mount that config directory
# into the container instead of copying tokens into the image.
# - If the service only supports env-based auth, keep the secret here in .env
# or inject it from your cloud harness at runtime.

# Required for LLM-as-judge evals (bun run test:eval)
ANTHROPIC_API_KEY=sk-ant-your-key-here
# Anthropic account-backed OAuth/session token fallback for non-mounted or
# headless environments. Keep this runtime-only in .env or your cloud secret
# harness; never pass it as a Docker build arg.
ANTHROPIC_AUTH_TOKEN=

# Anthropic API key fallback for evals or API-only environments
ANTHROPIC_API_KEY=

# OpenAI / Codex fallback for API-key environments
OPENAI_API_KEY=

# Gemini fallback for API-key environments
GEMINI_API_KEY=

# Optional custom endpoints for proxy or self-hosted routing
ANTHROPIC_BASE_URL=
OPENAI_BASE_URL=
GEMINI_BASE_URL=

# Add other service-specific runtime vars below as needed. Keep them out of the
# Dockerfile and out of build args.
18 changes: 17 additions & 1 deletion .github/docker/Dockerfile.ci
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
# Rebuild weekly via ci-image.yml, on Dockerfile changes, or on lockfile changes
FROM ubuntu:24.04

ARG BUN_VERSION=1.3.10
ARG BUN_INSTALL_SHA=bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

ENV DEBIAN_FRONTEND=noninteractive

# System deps
Expand All @@ -24,7 +29,18 @@ RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \

# Bun (install to /usr/local so non-root users can access it)
ENV BUN_INSTALL="/usr/local"
RUN curl -fsSL https://bun.sh/install | BUN_VERSION=1.3.10 bash
RUN tmpfile="$(mktemp)" \
&& curl -fsSL https://bun.sh/install -o "$tmpfile" \
&& actual_sha="$(sha256sum "$tmpfile" | awk '{print $1}')" \
&& if [ "$actual_sha" != "$BUN_INSTALL_SHA" ]; then \
echo "ERROR: bun install script checksum mismatch" >&2; \
echo " expected: $BUN_INSTALL_SHA" >&2; \
echo " got: $actual_sha" >&2; \
rm -f "$tmpfile"; \
exit 1; \
fi \
&& BUN_VERSION="${BUN_VERSION}" bash "$tmpfile" \
&& rm -f "$tmpfile"

# Claude CLI
RUN npm i -g @anthropic-ai/claude-code
Expand Down
14 changes: 13 additions & 1 deletion .github/workflows/evals-periodic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ jobs:
with:
fetch-depth: 0

- name: Mask provider secrets
shell: bash
run: |
for secret in \
"${{ secrets.ANTHROPIC_API_KEY }}" \
"${{ secrets.OPENAI_API_KEY }}" \
"${{ secrets.GEMINI_API_KEY }}"; do
if [ -n "$secret" ]; then
echo "::add-mask::$secret"
fi
done

- name: Fix bun temp
run: |
mkdir -p /home/runner/.cache/bun
Expand Down Expand Up @@ -126,4 +138,4 @@ jobs:
with:
name: eval-periodic-${{ matrix.suite.name }}
path: ~/.gstack-dev/evals/*.json
retention-days: 90
retention-days: 14
14 changes: 13 additions & 1 deletion .github/workflows/evals.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ jobs:
with:
fetch-depth: 0

- name: Mask provider secrets
shell: bash
run: |
for secret in \
"${{ secrets.ANTHROPIC_API_KEY }}" \
"${{ secrets.OPENAI_API_KEY }}" \
"${{ secrets.GEMINI_API_KEY }}"; do
if [ -n "$secret" ]; then
echo "::add-mask::$secret"
fi
done

# Bun creates root-owned temp dirs during Docker build. GH Actions runs as
# runner user with HOME=/github/home. Redirect bun's cache to a writable dir.
- name: Fix bun temp
Expand Down Expand Up @@ -144,7 +156,7 @@ jobs:
with:
name: eval-${{ matrix.suite.name }}
path: ~/.gstack-dev/evals/*.json
retention-days: 90
retention-days: 14

report:
runs-on: ubicloud-standard-2
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/security-policy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: Security Policy
on: [push, pull_request]

jobs:
security-policy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- run: bun install --frozen-lockfile
- name: Run security policy checks
run: bun run security:check
19 changes: 13 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
# Runtime secrets and local auth state
.env
.env.local
.env.*
!.env.example
extension/.auth.json
.claude.json

# Local development and generated state
node_modules/
browse/dist/
design/dist/
bin/gstack-global-discover
.gstack/
.gstack-worktrees/
.claude/skills/
.agents/
.factory/
.context/
extension/.auth.json
.gstack-worktrees/
/tmp/
*.log
*.bun-build
.env
.env.local
.env.*
!.env.example
supabase/.temp/

# Editor-local files that should stay off-repo
.vscode/launch.json
.vscode/extensions.json
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"chat.tools.terminal.autoApprove": {
"bun": true
}
}
47 changes: 47 additions & 0 deletions ABOUT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# About This Fork

This fork exists to harden agent-facing software packs against privacy leaks, manipulative product framing, and hidden operational drift. The original gStack is a large agent pack. This fork keeps the useful mechanics, but treats every skill, prompt, tool, and helper script as something that should be auditable for:

- outbound telemetry and hosted egress
- dark patterns and authority-driven persuasion
- hidden automation and silent behavioral drift
- brittle generation pipelines and stale packaged artifacts
- unsafe defaults that create dependency or pressure rather than informed use

## What This Fork Is For

This is a working base for auditing and hardening:

- skill packs
- prompt packs
- agent packs
- shell tool bundles
- browser automation helpers
- review and routing frameworks

If a pack can influence user behavior or move data off the machine, it ***should be testable*** under this same health harness.

## Operating Principles

1. Local-first by default<br>Hosted telemetry, update checks, and community dashboards should be optional at most, and easy to hard-disable.

2. No manipulation as product strategy<br>Do not rely on identity expansion, shame, urgency, status signaling, founder conversion, or dependency-building copy to drive adoption.

3. Auditability over charisma<br>Generated docs, shared preambles, and packaged prompts should be traceable back to source and checked continuously.

4. Reuse over one-off cleanup<br>Every problem class that appears more than once should become a harness rule, test fixture, or reusable policy check.

## Included Here

- a reusable stack-health harness
- fixture-backed manipulation-policy tests
- a one-switch no-egress mode for the active hosted paths in this repo
- documentation for reusing the same auditing model across other packs

## Long-Term Direction

The long-term goal is to use this repo as a reference implementation for auditing all skill, tool, and agent packs with the same basic question:

> Does this pack *respect user autonomy*, *keep data local* unless explicitly allowed, and *remain understandable* under change?

***If not, it should fail review, fail policy, and be rewritten until it does!***
3 changes: 3 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ bun install

Bun auto-loads `.env` — no extra config. Conductor workspaces inherit `.env` from the main worktree automatically (see "Conductor workspaces" below).

Treat `.env` as local-only state. Do not commit it or sync it through plain git. If you need the same secrets on multiple machines, use a secret manager, encrypted git workflow, Syncthing, or another local-only transport outside this repo.

### Test tiers

| Tier | Command | Cost | What it tests |
Expand All @@ -126,6 +128,7 @@ Bun auto-loads `.env` — no extra config. Conductor workspaces inherit `.env` f

```bash
bun test # Tier 1 only (runs on every commit, <5s)
bun run security:check # Repo security and local-state policy gate
bun run test:e2e # Tier 2: E2E only (needs EVALS=1, can't run inside Claude Code)
bun run test:evals # Tier 2 + 3 combined (~$4/run)
```
Expand Down
90 changes: 90 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# syntax=docker/dockerfile:1.7

ARG BASE_IMAGE=mcr.microsoft.com/playwright:v1.58.2-noble
ARG BUN_VERSION=1.3.10
ARG BUN_INSTALL_SHA=bab8acfb046aac8c72407bdcce903957665d655d7acaa3e11c7c4616beae68dd

FROM ${BASE_IMAGE} AS base

ARG BUN_VERSION
ARG BUN_INSTALL_SHA

SHELL ["/bin/bash", "-o", "pipefail", "-c"]

ENV DEBIAN_FRONTEND=noninteractive \
HOME=/root \
BUN_INSTALL=/usr/local \
PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \
PATH=/usr/local/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin

RUN apt-get update && apt-get install -y --no-install-recommends \
bash \
ca-certificates \
curl \
git \
jq \
python3 \
unzip \
&& rm -rf /var/lib/apt/lists/*

RUN mkdir -p /root/.claude /root/.codex /workspace

RUN cat <<'EOF' >/usr/local/bin/gstack-container-init
#!/usr/bin/env bash
set -euo pipefail

mkdir -p /root/.claude /root/.codex /root/.config /root/.cache /root/.local/share

# Persist Claude's top-level config file inside the Claude volume.
if [ ! -L /root/.claude.json ]; then
rm -f /root/.claude.json
ln -s /root/.claude/.claude.json /root/.claude.json
fi

# If Claude left only a backup, restore the newest backup as the primary config.
if [ ! -s /root/.claude/.claude.json ]; then
latest_backup="$(ls -1t /root/.claude/backups/.claude.json.backup.* 2>/dev/null | head -n 1 || true)"
if [ -n "$latest_backup" ]; then
cp "$latest_backup" /root/.claude/.claude.json
fi
fi

exec "$@"
EOF
RUN chmod +x /usr/local/bin/gstack-container-init

RUN tmpfile="$(mktemp)" \
&& curl -fsSL https://bun.sh/install -o "$tmpfile" \
&& actual_sha="$(sha256sum "$tmpfile" | awk '{print $1}')" \
&& if [ "$actual_sha" != "$BUN_INSTALL_SHA" ]; then \
echo "ERROR: bun install script checksum mismatch" >&2; \
echo " expected: $BUN_INSTALL_SHA" >&2; \
echo " got: $actual_sha" >&2; \
rm -f "$tmpfile"; \
exit 1; \
fi \
&& BUN_VERSION="${BUN_VERSION}" bash "$tmpfile" \
&& rm -f "$tmpfile"
RUN bun install -g @anthropic-ai/claude-code

WORKDIR /workspace

FROM base AS deps

COPY package.json bun.lock* ./
RUN bun install --frozen-lockfile 2>/dev/null || bun install

FROM deps AS build

COPY . .
RUN bun run build

FROM base AS runtime

COPY package.json bun.lock* ./
RUN bun install --frozen-lockfile 2>/dev/null || bun install

COPY --from=build /workspace /workspace

ENTRYPOINT ["/usr/local/bin/gstack-container-init"]
CMD ["bash"]
Loading