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
37 changes: 37 additions & 0 deletions .github/workflows/gitleaks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: gitleaks

# Secret-scanning guard for this PUBLIC repo. Runs on every push and PR so a
# credential can never land in history unnoticed. Uses the gitleaks CLI via
# its official Docker image (license-free for org-owned repos, unlike the
# gitleaks-action, which requires a paid license for organizations).
# The local .pre-commit-config.yaml catches most leaks before they are even
# committed; this CI job is the enforced, unbypassable backstop.

on:
push:
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
gitleaks:
name: Scan for committed secrets
runs-on: ubuntu-latest
steps:
- name: Checkout (full history)
uses: actions/checkout@v4
with:
fetch-depth: 0 # scan the entire commit history, not just HEAD

- name: Run gitleaks
run: |
docker run --rm -v "${{ github.workspace }}:/repo" \
ghcr.io/gitleaks/gitleaks:latest \
detect \
--source /repo \
--config /repo/.gitleaks.toml \
--redact \
--verbose \
--exit-code 1
31 changes: 31 additions & 0 deletions .gitleaks.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# gitleaks configuration for Build-With-Sumit/globus
#
# Extends the built-in default ruleset (covers AWS, GCP, Stripe, OpenAI,
# GitHub, Slack, private keys, generic high-entropy assignments, etc.) and
# adds an allowlist so the deliberately-sanitized example files and public
# identifiers don't produce false positives.
#
# Used by both the CI job (.github/workflows/gitleaks.yml) and the local
# pre-commit hook (.pre-commit-config.yaml).

title = "globus gitleaks config"

[extend]
useDefault = true

[allowlist]
description = "Sanitized examples + public identifiers are not secrets"

# Example/template config files ship placeholder values by design.
paths = [
'''config/\.env\.example''',
'''config/persona\.example\.md''',
]

# Values that look secret-shaped but are public or are obvious placeholders.
regexes = [
'''wDsJlOXPqcvIUKdLXjDs''', # public ElevenLabs library voice id ("Jarvis")
'''change-me(-root)?''', # placeholder DB passwords in .env.example
'''replace-with-32-byte-hex''', # placeholder SESSION_SECRET
'''your-[a-z-]+''', # generic "your-key-here" style placeholders
]
19 changes: 19 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Local secret-scanning guard. Blocks a commit if gitleaks finds a secret in
# the staged changes, so a credential never leaves your machine.
#
# One-time setup:
# pip install pre-commit
# pre-commit install
#
# Keep the pinned rev current with: pre-commit autoupdate
#
# NOTE: a local hook is bypassable (git commit --no-verify) and requires each
# contributor to install it. The authoritative, unbypassable scan is the
# gitleaks CI job in .github/workflows/gitleaks.yml, which re-scans the full
# history on every push and pull request.

repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.21.2
hooks:
- id: gitleaks
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ the cheapest path for dev iteration (~$0.0001 per 1K tokens).

## House rules

- **Never commit secrets.** Real keys/tokens/passwords/connection strings
live only in your untracked `.env` (or the MySQL `config` table) — never
in a tracked file, not even a doc or an example. A gitleaks CI job
(`.github/workflows/gitleaks.yml`) scans every push and PR, and
`.pre-commit-config.yaml` blocks a leak locally — run
`pip install pre-commit && pre-commit install` once. If a secret does get
committed, rotate it (scrubbing history does not un-leak an exposed key)
and open an issue.
- **Stdlib first.** New runtime deps need a reason in the PR description.
- **No emoji in source code or comments** unless the PR is specifically
about the user-facing UI text. They render inconsistently across
Expand Down
9 changes: 9 additions & 0 deletions config/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ GLOBUS_VOICE_LLM_SECRET=
GLOBUS_DAILY_CAP=500 # user messages per UTC day per member
GLOBUS_MAX_CONNECTIONS_PER_MEMBER=10
GLOBUS_VAULT_MAX_CHARS=200000
# Directory the offline vault auto-builder writes notes into (read by the
# vault-progress dashboard). Defaults to /opt/globus/vault/auto.
GLOBUS_VAULT_AUTO_DIR=

# ---- Single-tenant owner (optional) ----
# Email of the member who may trigger/read the Hermes background agents in
# a single-tenant deploy. Leave blank to fall back to GLOBUS_FIRST_MEMBER_EMAIL;
# if both are blank, owner-only agents stay locked until you set one.
AGENTS_OWNER_EMAIL=

# ---- Docker compose helpers (only read by docker-compose.yml,
# not by the app itself) ----
Expand Down
6 changes: 5 additions & 1 deletion server/agents_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,12 @@
# to trigger them, read their briefs, or see their live status. Once
# per-member agent copies exist, this constant becomes per-row data.
# (Sumit 2026-06-26.)
# Config-driven so the open-source build carries no personal default:
# set AGENTS_OWNER_EMAIL (or reuse GLOBUS_FIRST_MEMBER_EMAIL, the seed
# member) to nominate the single-tenant owner. If neither is set, no one
# is treated as owner — owner-only agents stay locked until configured.
_AGENTS_OWNER_EMAIL = (cfg("AGENTS_OWNER_EMAIL", "")
or "[email protected]").lower()
or cfg("GLOBUS_FIRST_MEMBER_EMAIL", "")).lower()


def _is_agents_owner(email):
Expand Down
11 changes: 7 additions & 4 deletions server/vault_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@
intermittent ElevenLabs 'upstream error' Sumit reported
2026-06-24).

Module deps: db_read (db_helpers). Reads /opt/buildwithsumit/vault/auto
directly for the notes-by-type count, defensive against perm errors
Module deps: db_read (db_helpers). Reads the vault auto-build dir
(GLOBUS_VAULT_AUTO_DIR, default /opt/globus/vault/auto) directly for the
notes-by-type count, defensive against perm errors
(the auto-builder runs as root and can create subdirs blocked to
www-data — we skip unreadable subdirs rather than 500'ing).
"""
from __future__ import annotations
import os
import time
from db_helpers import db_read
from db_helpers import db_read, cfg


VAULT_SOURCE_META = {
Expand Down Expand Up @@ -176,7 +177,9 @@ def _add(src_type, extracted, processed):
# poll (the live page parses the response as JSON; an HTML 500
# error there breaks the whole dashboard).
notes_by_type = {}
base = "/opt/buildwithsumit/vault/auto"
# Directory the offline vault auto-builder writes notes into. Config
# knob so the open-source build isn't pinned to a production path.
base = cfg("GLOBUS_VAULT_AUTO_DIR", "/opt/globus/vault/auto")
try:
subdirs = os.listdir(base) if os.path.isdir(base) else []
except OSError:
Expand Down
Loading