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
33 changes: 33 additions & 0 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Repository Guidelines

## Project Structure & Module Organization

The docs site runs on Docusaurus. Author new or updated guides in `docs/` and keep contributor-facing notes in `docs-contributing/`. Historical content lives in `versioned_docs/`, paired with `versioned_sidebars/` for navigation metadata. Custom React or styling tweaks reside in `src/` (`components/`, `pages/`, `theme/`), while shared assets such as images, downloads, and redirects belong under `static/`. Park work-in-progress material inside `docs-drafts/` to prevent accidental publication.

## Build, Test, and Development Commands

Run `npm start` (or `make npm.dev`) to spin up the live-reloading docs server on `localhost:3000`. `npm run build` (mirrored by `make npm.build`) generates the production-ready static bundle in `build/` and surfaces MDX or sidebar errors. Enforce content quality with `npm run lint` or `make npm.lint`, which executes `markdownlint-cli2` across `docs/**` and `versioned_docs/**`.

```bash
npm run lint
```

Ensure `npm run build` exits without error before committing any changes.

## Coding Style & Naming Conventions

Use YAML front matter with at least `title` and `description` (and `slug` or `sidebar_position` when needed) to keep navigation orderly. Write concise Markdown/MDX in sentence case headings, wrap prose near 100 characters to ease reviews, and prefer lists or tables for procedural steps. Tag code fences with a language hint and surface commands with shell blocks. Name reusable MDX components in PascalCase and place them under `src/components/`, and store shared assets in kebab-case filenames (for example `rolling-deploy.png`).

Further coding and style guidelines are stated on `docs-contributing/STYLE_GUIDE.md`

## Testing Guidelines

Run `npm run lint` before pushing; suppress rules only when `.markdownlint.json` documents the exception. Follow with `npm run build` to catch broken imports, invalid images, or sidebar regressions. For parity with containerized checks, `make test` executes the nginx configuration validation used in CI.

## Commit & Pull Request Guidelines

Use the Conventional Commits style seen in history—`docs(scope): concise summary`—to keep automation predictable. Reference issues or Linear tickets in the body, describe the user-facing impact, and call out the docs sections affected. Include screenshots or GIFs when the UI or illustrations change, and add deploy preview URLs so reviewers can validate navigation, search, and syntax highlighting interactively.

## Versioning

The versioning schema for new releases in the `versioned_docs` and `versioned_sidebars` as detailed in the `docs-contributing/VERSIONING.md`.
50 changes: 50 additions & 0 deletions docs/DOCUMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Docs Workspace Architecture

## Tech Stack Snapshot

- Docusaurus 3 (`@docusaurus/core` 3.9.x) with the classic preset, Algolia search, Mermaid diagrams, and `docusaurus-theme-openapi-docs`.
- React 18 components backed by Prism for code highlighting (GitHub/Dracula themes plus Elixir, Java, Groovy extra languages).
- Builds require Node ≥18 and rely on `markdownlint-cli2` for Markdown quality checks.
- The Dockerfile performs a multi-stage build: Node-based asset compilation followed by an Nginx runtime image that serves `/usr/share/nginx/html`.

## Directory Layout & Ownership

- `docs/`: Active Cloud (current) documentation. Keep front matter (`title`, optional `slug`, `sidebar_position`) with sentence-case headings. Subfolders mirror sidebar categories (see `sidebars.js`).
- `versioned_docs/`: Frozen snapshots per edition (`version-CE`, `version-EE`, etc.). Generated via `npm run docusaurus docs:version <label>` and tracked in `versions.json`, `versioned_sidebars/` and `docusaurus.config.js`.
- `docs-contributing/`: Meta documentation—contribution flow, reviewing steps, style guide, UI inventory. Reference these before editing content.
- `docs-drafts/`: Sandbox for WIP material; not routed to production. Move items into `docs/` or versioned folders once approved.
- `src/`: React/MDX enhancements. `components/` stores reusable blocks, `theme/` overrides Docusaurus theme components, `pages/` hosts custom routes, and `css/` contains `custom.css`.
- `static/`: Assets copied verbatim to the output (images, downloads, redirects). Use kebab-case filenames.
- `default.conf`: Nginx config used both locally (Docker) and in deploy artifacts to mount the generated static site.

## Content & Navigation Workflow

1. Draft in `docs-drafts/` or an edition-specific folder with required front matter.
2. Wire navigation via `sidebars.js` for curated sections or rely on autogenerated categories (see existing patterns).
3. Re-run `npm run lint` to enforce Markdown style; `.markdownlint.json` relaxes line length but keeps other defaults.
4. Execute `npm start` for live preview (`localhost:3000`) and validate links visually.
5. Build with `npm run build` (or `make npm.build`) before hand-off; build throws on broken images/links.
6. For release branches, snapshot content with `npm run docusaurus docs:version <Edition>` and commit the generated folder plus `versions.json`.

## Tooling & Commands

- `npm start` / `make npm.dev`: Dev server with hot reload and local search.
- `npm run build` / `make npm.build`: Production bundle under `build/`.
- `npm run lint` / `make npm.lint`: Runs `markdownlint-cli2` across `docs/**` and `versioned_docs/**`; suppressions must be justified.
- `make test`: Invokes the repo-wide Docker image to validate `nginx -t`, mirroring CI smoke checks.
- `npm run docusaurus ...`: Access the Docusaurus CLI (e.g., `write-heading-ids`, `swizzle` for component overrides).

## Theming, Integrations & Data Sources

- Configured in `docusaurus.config.js`: base URL defaults to `/`, production URL `https://docs.semaphore.io`.
- Editions and labels defined under `presets[0].docs.versions`; the `docsVersionDropdown` in the navbar reads from here.
- OpenAPI integration fetches `https://docs.semaphore.io/v2/api-spec/openapi.yaml` at build time and emits content in `docs/openapi-spec/`; offline builds need the spec cached manually.
- PostHog and Google Analytics tags are wired for production only; no extra steps needed locally.
- Algolia search (index `v2-sxmoon`) is configured but relies on back-end indexing jobs; run `npm run build` to generate crawlable markup.

## Troubleshooting Notes

- Sandbox environments without network access will cause the OpenAPI plugin to fail; temporarily comment the plugin block or pre-populate `docs/openapi-spec/` if necessary.
- If Markdown build errors point to broken images, ensure assets exist under `static/` and are addressed with absolute `/img/...` paths.
- Sidebar mismatches typically stem from identifier changes; keep doc IDs stable or update references in `sidebars.js` and cross-links.
- Remember this docs app lives under `docs/` in the mono-repo; the top-level `Makefile` (included via `include ../Makefile`) may inject additional CI targets—review upstream when debugging pipeline issues.
3 changes: 2 additions & 1 deletion docs/docs/reference/openid.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ Sure, here is the reordered list presented in a table with three columns: Claim,
|-------------|-----------------------------------------------------------|--------------------------------------|
| iss | The issuer of the token. The full URL of the organization | `https://<org-name>.semaphoreci.com` |
| aud | The intended audience of the token. The full URL of the organization | `https://<org-name>.semaphoreci.com` |
| sub | The subject of the token. A combination of org, project, repository, and git reference for which this token was issued<br/>Template:<br/> `org:<org-name>:`<br/>`project:{project-id}:`<br/>`repo:{repo-name}:`<br/>`ref_type:{branch or pr or tag}:`<br/>`ref:{git_reference}`<br/><br/>**Note**: This field often exceeds Google Cloud's 127-byte limit for attribute mapping. For Google Cloud integration, use individual claims like `repo`, `branch`, or `prj` instead. | `org:{org-name}:`<br/>`project:936a5312-a3b8-4921-8b3f-2cec8baac574:`<br/>`repo:web:`<br/>`ref_type:branch:`<br/>`ref:refs/heads/main` |
| sub | The subject of the token. A combination of org, project, repository, and git reference for which this token was issued<br/>Template:<br/> `org:<org-name>:`<br/>`project:{project-id}:`<br/>`repo:{repo-name}:`<br/>`ref_type:{branch or pr or tag}:`<br/>`ref:{git_reference}`<br/><br/>**Note**: This field often exceeds Google Cloud's 127-byte limit for attribute mapping. Use the `sub127` claim (outlined below) or individual claims like `repo`, `branch`, or `prj` when you need a shorter value. | `org:{org-name}:`<br/>`project:936a5312-a3b8-4921-8b3f-2cec8baac574:`<br/>`repo:web:`<br/>`ref_type:branch:`<br/>`ref:refs/heads/main` |
| sub127 | Compact subject (`org:project_id:repo:ref_type:ref`) sanitized for attribute mapping. Removes `:` characters, shortens ref types to two letters (`br`, `tg`, `pr`), drops the leading `refs/` prefix from refs, and caps the final value at 127 characters. Component limits: `org ≤ 25`, `repo ≤ 25`, `ref ≤ 35` characters (project ID stays at 36 characters). Designed for providers with strict length limits such as Google Cloud. | `acme:936a5312-a3b8-4921-8b3f-2cec8baac574:web:br:heads/main` |
| exp | The UNIX timestamp when the token expires | `1660317851` |
| iat | The UNIX timestamp when the token was issued | `1660317851` |
| nbf | The UNIX timestamp before which the token is not valid | `1660317851` |
Expand Down
5 changes: 2 additions & 3 deletions docs/docs/using-semaphore/openid.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ Next, we need to map fields from the Semaphore OIDC provider to Google Cloud att

2. Use the following template to grant Google Cloud access to the identity pool created in Step 1.

**Note**: Google Cloud has a 127-byte limit for mapped attributes. Semaphore's JWT subject can exceed this limit, so we'll use shorter JWT claims for the mapping.
**Note**: Google Cloud has a 127-byte limit for mapped attributes. Semaphore's `sub` claim can exceed this limit, but each token now also includes a compact `sub127` claim that stays under 127 characters (with `org` and `repo` capped at 25 characters and `ref` capped at 35). The example below keeps using shorter individual claims for clarity, yet you can map `google.subject=assertion.sub127` if you prefer to rely on the subject value.

Replace:
- `<REPOSITORY>` with your repository name, e.g. `web`
Expand All @@ -221,7 +221,7 @@ Next, we need to map fields from the Semaphore OIDC provider to Google Cloud att
- Maps the repository name from the JWT to `google.subject`
- Maps the branch name from the JWT to `google.branch`
- Uses attribute conditions to verify the specific repository, branch, and project from the JWT claims
- Avoids the 127-byte limit by using shorter JWT claims instead of the full subject
- Avoids the 127-byte limit by using shorter JWT claims (or the compact `sub127` claim) instead of the full subject

</Steps>

Expand Down Expand Up @@ -385,4 +385,3 @@ See the [OIDC token reference page](../reference/openid) to learn how the OIDC t

- [How to integrate Semaphore with Okta](./okta)
- [Semaphore organizations](./organizations)

2 changes: 1 addition & 1 deletion secrethub/.credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
#
## Readability Checks
#
{Credo.Check.Readability.AliasOrder, exit_status: 0},
{Credo.Check.Readability.AliasOrder, false},
{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 120},
Expand Down
24 changes: 24 additions & 0 deletions secrethub/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Repository Guidelines

## Project Structure & Module Organization
Core modules live under `lib/secrethub/**` with the OTP entry point in `lib/secrethub.ex`. Generated gRPC stubs populate `lib/internal_api` and `lib/public_api`; refresh them via the proto Make targets. Configuration sits in `config/*.exs`, assets and keys in `priv/`, and ExUnit tests mirror the tree in `test/**` with helpers in `test/support/`. Versioned `.proto` sources live in `proto/`, with helper scripts under `scripts/`.

## Build, Test, and Development Commands
- `mix deps.get` installs Elixir dependencies for the current `MIX_ENV`.
- `mix compile` builds the app locally; run before committing generated code.
- `mix test` runs ExUnit; narrow scope with `mix test path/to/file_test.exs`.
- `mix credo --strict` applies the lint rules enforced in `.credo.exs`.
- `make test.ex.setup` boots Docker services, runs migrations, and seeds the test DB.
- `make pb.gen.internal` / `make pb.gen.public` regenerate gRPC stubs via the scripts in `scripts/`.

## Coding Style & Naming Conventions
Run `mix format` before every commit; it enforces `.formatter.exs` and two-space indentation. Keep modules under the `Secrethub.*` namespace, align pipelines, and name files with `snake_case.ex` and tests with `*_test.exs`. Address Credo findings locally so CI stays clean.

## Testing Guidelines
Tests use ExUnit with shared helpers in `test/support`. Prefer descriptive names (`test "revokes token"`). Use the provided data-case helpers for database scenarios and add gRPC fixtures under `test/support` when mocking upstream calls. Ensure suites pass with `mix test`; CI toggles the bundled `JUnitFormatter` when needed.

## Commit & Pull Request Guidelines
History follows conventional commits such as `feat(guard): add posthog integration` or `fix: lock go tool versions`. Start with an imperative summary, include the relevant scope, and reference issues in parentheses (e.g. `(#598)`). Pull requests should explain intent, list validation steps (`mix test`, `mix credo`), and attach screenshots or payload samples for behavioral changes.

## Security & Configuration Tips
Environment variables defined in the `Makefile` inject credentials for Postgres, RabbitMQ, and external services; never commit real secrets, just use the provided placeholders. Keep generated protobuf code aligned with upstream schemas and audit new dependencies with `mix deps.unlock --check-unused`.
44 changes: 44 additions & 0 deletions secrethub/DOCUMENTATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Internal Architecture Notes

## What This Service Does
`secrethub` is the semaphore secrets service. It stores encrypted organization, project, and deployment secrets in Postgres, exposes internal gRPC APIs used by other backend services, serves a public gRPC API for agents/CLI, and hosts an OpenID Connect (OIDC) HTTP endpoint that issues scoped JWTs for third parties. Secrets are always persisted encrypted-at-rest and decrypted on demand through the external encryptor service.

## Runtime Topology
- Entry point: `Secrethub.Application` boots the supervision tree, initialises the feature provider, and conditionally starts gRPC servers, OIDC HTTP, the OpenID key manager, and AMQP consumers based on `START_*` env vars.
- Persistence: `Secrethub.Repo` wraps Postgres via Ecto; migrations live in `priv/repo/migrations`.
- Caches: Cachex stores RBAC permission checks (`:auth_cache`), feature flags, and OIDC usage counters; eviction windows are configured in `application.ex`.
- GRPC servers: `Secrethub.InternalGrpcApi`, `Secrethub.PublicGrpcApi`, and `Secrethub.ProjectSecretsPublicApi` run under `GRPC.Server.Supervisor`. Health checks use `GrpcHealthCheck.Server`.
- OpenID Connect: `lib/secrethub/open_id_connect/**` hosts a Plug stack served by Cowboy plus a `KeyManager` GenServer that persists rotating signing keys under `priv/openid_keys_in_tests` for local runs.

## Module Map
- Core domain: `lib/secrethub/secret.ex` holds the main Ecto schema and CRUD logic; `project_secrets/**` contains a parallel flow for project-scoped secrets, while `deployment_targets/**` manages deploy target secrets.
- Security helpers: `Secrethub.Auth` talks to the RBAC gRPC service with Cachex-backed memoization; `Secrethub.Encryptor` wraps the external encryptor gRPC API.
- Integrations: `FeatureHubProvider` queries the feature service; `ProjecthubClient` fetches project metadata; `OwnerDeletedConsumer` listens for AMQP deletion events to clean up secrets.
- Utilities: `model/**` defines plain structs used during (de)serialization; `level_gen/**` provides helpers for assembling multi-level env/file payloads; `utils.ex` is a catch-all map transformer used by the gRPC APIs.
- Generated stubs: `lib/internal_api/**` and `lib/public_api/**` are regenerated from the repos referenced in the `Makefile` (`make pb.gen.*`).

## Data Model Highlights
- `secrets` table stores `content_encrypted`; the decrypted `content` map is virtual and shaped via `Secrethub.Model.Content` with nested `EnvVar` and `File`.
- Project secrets use a separate schema (`project_secrets/secret.ex`) with feature-flag-aware visibility filtering driven by `FeatureProvider`.
- Secret policies include metadata such as `org_config`, `all_projects`, `project_ids`, and audit fields (`created_by`, `used_by`). Access checks combine RBAC results with feature flags before persisting changes.

## External Services & Configuration
- gRPC endpoints are configured in `config/*.exs`: `:encryptor`, `:feature_api_endpoint`, `:rbac_grpc_endpoint`, `:projecthub_grpc_endpoint`.
- AMQP connection details, Postgres credentials, and service ports are defined in the repo Makefile and surfaced as environment variables (`POSTGRES_DB_*`, `AMQP_URL`, `BASE_DOMAIN`, `START_*` flags).
- Feature flags can be bootstrapped locally by setting `FEATURE_YAML_PATH`, which attaches the configured provider to the supervision tree.

## Build & Local Development
- Core commands: `mix deps.get`, `mix compile`, `mix test`, `mix credo --strict`.
- Integration flows (DB/protos) rely on Docker: `make test.ex.setup` provisions Postgres/RabbitMQ, runs migrations, and seeds data; `make pb.gen.internal` / `make pb.gen.public` clone the internal API repos and regenerate proto stubs (requires GitHub access and Docker).
- OpenID HTTP port and gRPC ports are read from config (`config/dev.exs`); adjust if ports clash with other services.

## Testing Aids
- ExUnit helpers live in `test/support`: `DataCase` bootstraps the DB sandbox, `Factories` build fixture structs, and `FakeServices` starts in-process gRPC mocks when `start_stub_grpc_services?/0` evaluates to true (dev/test envs).
- Tests commonly assert on decrypted payloads using `Secrethub.Encryptor.decrypt_secret/1`; prefer the factory helpers to avoid manual encryption.
- CI uses `JUnitFormatter` (configured in `mix.exs`) and expects `mix test` and `mix credo` to pass before merging.

## Observability & Troubleshooting
- Logging: Sentry backend is registered in `Application.start/2`; ensure SENTRY_DSN is present in non-local envs.
- Metrics: Watchman timings gauge gRPC calls, encryption latency, and feature fetch success/failure (`watchman` client).
- Feature cache invalidation: `FeatureProviderInvalidatorWorker` monitors YAML-backed providers; ensure `FEATURE_YAML_PATH` is mounted in dev if you rely on live refresh.
- When debugging auth issues, inspect `:auth_cache` entries (Cachex) and verify RBAC gRPC connectivity; the cache keys are SHA-256 hashes of the payload.
2 changes: 1 addition & 1 deletion secrethub/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ENV MIX_ENV=$BUILD_ENV

RUN echo "Build for $MIX_ENV environment started"

RUN apk update && apk add --no-cache build-base git python3 curl openssh
RUN apk update && apk add --no-cache build-base git python3 curl bash openssh

RUN mkdir -p ~/.ssh
RUN touch ~/.ssh/known_hosts
Expand Down
4 changes: 3 additions & 1 deletion secrethub/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ services:
tty: true
volumes:
- .:/app
- /app/deps
- /app/_build

links:
- db:db
Expand All @@ -46,7 +48,7 @@ services:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: "the-cake-is-a-lie"

rabbitmq:
image: rabbitmq:3-management
ports:
Expand Down
Loading