> For the complete documentation index, see [llms.txt](https://docs.xenoraa.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.xenoraa.com/xenoraa-changelog-and-architecture/v10.0.8-secure-github-actions-ci-cd.md).

# v10.0.8 — Secure GitHub Actions CI/CD

> **Released:** 14 August 2026\
> **Production release:** `c8c6da1`\
> **Validated release documentation:** `a896280`

## Overview

Xenoraa now deploys from GitHub through a **self-hosted production runner** rather than accepting inbound SSH connections from GitHub-hosted runners or Manus. A push to `main` runs CI validation, performs the production deployment locally on the server only after validation succeeds, verifies the `/up` health endpoint, and records the deployed release in Sentry.

## Architecture

```
Developer push to main
        |
        v
GitHub Actions validation runner
  - Composer install
  - npm build
  - PHP syntax lint
  - PostgreSQL migrations
  - Unit tests
        |
        v
Self-hosted runner: xenoraa-production
  - outbound HTTPS connection to GitHub
  - runs as xenoraa-runner
        |
        v
sudo /usr/local/sbin/xenoraa-deploy-main
  - locked production deployment
  - runs application work as xenoraa-deploy
  - fetches origin/main and deploys
        |
        v
Health check: https://xenoraa.com/up
```

## What Changed

| Area                 | Implementation                                                                                                                                                                                                                                        |
| -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Deployment trigger   | A validated push to `main` deploys production automatically. `develop` and pull requests receive CI validation without production deployment.                                                                                                         |
| Runner model         | Added the persistent systemd service `actions.runner.goesscay-xenoraa.xenoraa-production.service` under the `xenoraa-runner` system account.                                                                                                          |
| Deployment privilege | `xenoraa-runner` has one passwordless sudo permission only: `/usr/local/sbin/xenoraa-deploy-main`.                                                                                                                                                    |
| Release procedure    | The root-owned deployment command serializes releases with a lock, fetches `origin/main`, installs dependencies, builds assets, runs migrations, rebuilds Laravel caches, updates `SENTRY_RELEASE`, reloads PHP-FPM, and restarts Supervisor workers. |
| CI gates             | Syntax lint, dependency installation, frontend build, fresh PostgreSQL migration, and Unit tests are required before production deployment.                                                                                                           |
| Feature suite        | Existing Feature tests run for visibility only until they are made hermetic; they currently depend on historical redirects and production-tenant assumptions.                                                                                         |

## Security Improvements

The new approach removes the need for GitHub-hosted runners to reach production port 22. The server initiates the only GitHub Actions connection over outbound HTTPS. The `xenoraa-deploy` account is used locally for repository work, has no inbound SSH authorized keys, and holds only the GitHub deploy key required to pull the repository.

The legacy `SSH_PRIVATE_KEY` and `XENORAA_PROD_DEPLOY_KEY` repository secrets were deleted. Legacy GitHub Actions and Manus public keys were removed from root SSH authorization, the obsolete forced-command deployment key was removed, and the root GitHub deployment key and superseded deploy-user sudo rule were retired. The Hostinger recovery key and the unrelated `GITBOOK_TOKEN` remain.

## Verification

GitHub Actions workflow run **#3, attempt #3** completed successfully. The self-hosted runner deployed release `c8c6da1`, and the production health endpoint returned:

```json
{"status":"ok","app":"xenoraa"}
```

## Operational Rules

1. Deploy to production by pushing an approved change to `main`.
2. Do not restore GitHub-hosted SSH deployment, root deployment keys, SSH deployment secrets, or Manus SSH keys.
3. Keep application work on `develop` or feature branches until it is ready for CI validation and production release.
4. Keep the self-hosted runner service enabled and monitor its status with `systemctl status actions.runner.goesscay-xenoraa.xenoraa-production.service`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.xenoraa.com/xenoraa-changelog-and-architecture/v10.0.8-secure-github-actions-ci-cd.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
