{"id":50587778,"url":"https://github.com/always-further/runseal","last_synced_at":"2026-06-05T07:30:43.356Z","repository":{"id":360177890,"uuid":"1249024111","full_name":"always-further/runseal","owner":"always-further","description":"Sealed execution environment for GitHub Actions. ","archived":false,"fork":false,"pushed_at":"2026-06-03T19:13:58.000Z","size":6359,"stargazers_count":6,"open_issues_count":1,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-03T20:11:20.849Z","etag":null,"topics":["actions","isolation","sandbox","security","supply-chain-security"],"latest_commit_sha":null,"homepage":"https://nono.sh ","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/always-further.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-25T09:18:51.000Z","updated_at":"2026-06-03T19:13:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/always-further/runseal","commit_stats":null,"previous_names":["always-further/runseal"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/always-further/runseal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/always-further%2Frunseal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/always-further%2Frunseal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/always-further%2Frunseal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/always-further%2Frunseal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/always-further","download_url":"https://codeload.github.com/always-further/runseal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/always-further%2Frunseal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33934339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-05T02:00:06.157Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["actions","isolation","sandbox","security","supply-chain-security"],"created_at":"2026-06-05T07:30:42.886Z","updated_at":"2026-06-05T07:30:43.347Z","avatar_url":"https://github.com/always-further.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cpicture\u003e\n  \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"./assets/banner-dark.png\"\u003e\n  \u003cimg alt=\"Runseal logo\" src=\"./assets/banner-light.png\" width=\"1000px\" style=\"max-width: 100%;\"\u003e\n\u003c/picture\u003e\n\n\u003cp\u003e\n  From the creator of\n  \u003ca href=\"https://sigstore.dev\"\u003e\u003cstrong\u003eSigstore\u003c/strong\u003e\u003c/a\u003e\n  \u003cbr/\u003e\n  \u003csub\u003eThe standard for secure software attestation, used by PyPI, npm, brew, and Maven Central\u003c/sub\u003e\n\u003c/p\u003e\n  \u003ca href=\"https://discord.gg/pPcjYzGvbS\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/Chat-Join%20Discord-7289da?style=for-the-badge\u0026logo=discord\u0026logoColor=white\" alt=\"Join Discord\"/\u003e\n  \u003c/a\u003e\n   \u003ca href=\"https://alwaysfurther.ai/careers\"\u003e\n      \u003cimg src=\"https://img.shields.io/badge/We're_Hiring-Join_the_team-ff4f00?style=for-the-badge\u0026logo=githubsponsors\u0026logoColor=white\" alt=\"We're hiring\"/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003c/div\u003e\n\nRunseal was built to solve the problem of software supply chain attacks that are often triggered from GitHub Actions-based exploits.\n\nBy using [nono's](https://github.com/always-further/nono) strong kernel-enforced sandboxing, runseal can protect secrets/tokens, sensitive files, and network access from untrusted or malicious code, while still allowing necessary software engineering operations through a flexible policy system.\n\nThe project is developed by the engineers behind [sigstore](https://sigstore.dev) and [nono](https://nono.sh).\n\n## What Runseal Does\n\n- Replace raw secrets within a workflow with phantom credentials that are useless if leaked\n- Protects sensitive files and secrets from exfiltration by untrusted code in CI\n- L7 network filtering to lock down network access by HTTP method and path\n- Uses `nono` TLS interception so HTTPS requests can be filtered by method/path\n- Blocks network by default unless policy explicitly allows a host or credential route\n- Restricts filesystem reads and writes to declared paths\n- Cryptographic audit captured outside of the sandbox for all network requests, credential injections, and filesystem access\n\n## Quick Start\n\n```yaml\nname: Publish\n\non:\n  workflow_dispatch:\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - uses: always-further/runseal@v1\n        with:\n          run: npm publish\n          policy: |\n            fs:\n              read: [\".\"]\n              write: []\n            network:\n              mode: filtered\n            access:\n              npm:\n                secret: NPM_TOKEN\n                url: https://registry.npmjs.org\n                allow:\n                  - PUT /**\n        env:\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}\n```\n\nIn this example, `npm publish` can read the repository, cannot write to paths\nnot listed in `fs.write`, cannot use general network access, and can only use\n`NPM_TOKEN` through the Runseal/nono proxy for allowed HTTPS requests to\n`registry.npmjs.org`.\n\n## Policy Format\n\nRunseal policy is YAML passed through the `policy` input.\n\n```yaml\nfs:\n  read:\n    - \".\"\n    - \"$HOME/.cache/my-tool\"\n  write:\n    - \"./dist\"\n\nnetwork:\n  mode: filtered\n  allow:\n    - api.github.com\n\naccess:\n  deploy:\n    secret: DEPLOY_TOKEN\n    url: https://api.example.com\n    allow:\n      - POST /v1/deployments\n      - GET /v1/deployments/*\n```\n\n### Filesystem Access\n\n`fs.read` lists paths the command can read. `fs.write` lists paths the command\ncan write.\n\nKeep these narrow. For example, a deploy step often only needs to read `./dist`\nand a config file, and may not need write access at all.\n\n```yaml\nfs:\n  read: [\"./dist\", \"./fly.toml\"]\n  write: []\n```\n\n### Network Access\n\nRunseal expects `network.mode: blocked` or `network.mode: filtered`.\n\nAdd `network.allow` only for unauthenticated hosts the command must reach. Hosts\nused by access grants are added to the generated `nono` profile automatically.\n\n```yaml\nnetwork:\n  mode: filtered\n  allow:\n    - api.github.com\n```\n\n### Access Grants\n\nEach key under `access` is a named grant. `secret` is the environment variable\ncontaining the real secret, `url` is the service base URL, and `allow` lists the\nHTTP routes where the secret may be injected. Runseal masks the secret in logs,\nwrites it to a private file, removes it from the child environment, and\nconfigures `nono` to inject it through the local proxy.\n\n```yaml\naccess:\n  fly:\n    secret: FLY_API_TOKEN\n    url: https://api.machines.dev\n    allow:\n      - POST /v1/apps/*/machines\n```\n\nThe sandboxed command receives a phantom credential for SDK compatibility. The\nreal secret remains outside the sandbox and is only inserted by the proxy when\nthe host and endpoint policy match.\n\n### HTTPS Endpoint Filtering\n\n`allow` restricts access use by HTTP method and path. Matching is allow-list\nbased.\n\n```yaml\nallow:\n  - POST /v1/apps/*/releases\n  - GET /v1/apps/*/status\n```\n\nRunseal relies on `nono` TLS interception for this. The `nono` proxy creates an\nephemeral trust bundle and injects standard CA environment variables into the\nsandboxed process, so common HTTPS clients can connect through the proxy while\nstill allowing L7 policy enforcement.\n\n## Common Recipes\n\n### Run Tests With No Network\n\n```yaml\n- uses: always-further/runseal@v1\n  with:\n    run: npm test\n    policy: |\n      fs:\n        read: [\".\", \"./node_modules\"]\n        write: [\"./coverage\"]\n      network:\n        mode: blocked\n```\n\n### Build With Package Registry Access\n\n```yaml\n- uses: always-further/runseal@v1\n  with:\n    run: npm ci\n    policy: |\n      fs:\n        read: [\".\"]\n        write: [\"./node_modules\"]\n      network:\n        mode: filtered\n        allow:\n          - registry.npmjs.org\n```\n\n### Deploy With A Sealed Token\n\n```yaml\n- uses: always-further/runseal@v1\n  with:\n    run: ./scripts/deploy.sh\n    policy: |\n      fs:\n        read: [\"./dist\", \"./deploy.yaml\"]\n        write: []\n      network:\n        mode: filtered\n      access:\n        deploy:\n          secret: DEPLOY_TOKEN\n          url: https://deploy.example.com\n          allow:\n            - POST /v1/releases\n            - GET /v1/releases/*\n  env:\n    DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}\n```\n\n## Inputs\n\n| Input | Required | Default | Description |\n| --- | --- | --- | --- |\n| `run` | Yes | none | Command to execute inside the sandbox. |\n| `policy` | No | empty | Runseal policy YAML. Prefer this for new workflows. |\n| `fs-read` | No | empty | Comma-separated read paths when `policy` is not set. |\n| `fs-write` | No | empty | Comma-separated write paths when `policy` is not set. |\n| `network` | No | `blocked` | Network policy when `policy` is not set: `blocked` or comma-separated domains. |\n| `runseal-version` | No | `latest` | Runseal release version to install. Accepts `v0.1.0` or `0.1.0`. |\n| `nono-version` | No | `latest` | nono release version to install. Accepts `v0.1.0` or `0.1.0`. |\n| `verify-attestations` | No | `true` | Verify GitHub artifact attestations for downloaded release assets. |\n| `audit` | No | `false` | Set to `artifact` or `true` to upload nono audit evidence as a GitHub Actions artifact. |\n\n## Audit Evidence\n\nRunseal can export the nono audit session for a sandboxed command:\n\n```yaml\n- uses: always-further/runseal@v1\n  with:\n    run: npm rebuild\n    audit: artifact\n    policy: |\n      fs:\n        read: [\".\", \"./node_modules\"]\n        write: [\"./node_modules\"]\n      network:\n        mode: blocked\n```\n\nWhen enabled, Runseal captures the new nono audit session after the command\nfinishes and uploads a `runseal-audit` artifact containing:\n\n- `summary.md`\n- one JSON file per detected nono audit session\n\nAudit export runs before Runseal returns the sandboxed command's exit status, so\nfailed or denied commands can still produce audit evidence.\n\n## Supply Chain Verification\n\nThe installer verifies release downloads in two layers:\n\n- SHA-256 checksum from the release `SHA256SUMS` file\n- GitHub artifact attestation proving the asset was built by the expected\n  repository and tag, for both `always-further/runseal` and `always-further/nono`\n\nAttestation verification uses `gh attestation verify` and is enabled by default.\nSet `verify-attestations: false` only for local testing or emergency fallback.\n\n## Requirements\n\n- Linux x86_64 GitHub-hosted runner\n- `gh` CLI available on the runner for attestation verification\n- Published release assets for both Runseal and `nono`\n\nRelease assets are expected to use this naming scheme:\n\n- `runseal-v\u003cversion\u003e-x86_64-unknown-linux-gnu.tar.gz`\n- `nono-v\u003cversion\u003e-x86_64-unknown-linux-gnu.tar.gz`\n- `SHA256SUMS`\n\n## Development\n\n```bash\nmake ci\n```\n\n`make ci` runs `make lint` and `make test` — the same Rust checks as the [CI workflow](.github/workflows/ci.yml).\n\n```bash\nmake lint       # clippy + fmt check\nmake test       # unit tests only\nmake fmt        # format code\nmake audit      # cargo audit (run make audit-install first)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falways-further%2Frunseal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falways-further%2Frunseal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falways-further%2Frunseal/lists"}