This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.rs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
@@ -0,0 +1,16 @@
|
||||
# Shared
|
||||
ENUXIA_AGENT_SOCKET=/run/user/1000/enuxia-frappe-aio/agent.sock
|
||||
ENUXIA_COMPOSE_PROJECT=my-frappe-project
|
||||
ENUXIA_SITE_NAME=site.example.test
|
||||
|
||||
# Agent only
|
||||
ENUXIA_AGENT_STATE_DB=/home/user/.local/state/enuxia-frappe-aio/agent.sqlite3
|
||||
ENUXIA_DOCKER_HOST=unix:///var/run/docker.sock
|
||||
|
||||
# Controller only
|
||||
ENUXIA_INSTANCE_NAME=my-frappe-instance
|
||||
ENUXIA_TARGET_NAME=local-node
|
||||
ENUXIA_FRAPPE_VERSION=v16
|
||||
ENUXIA_BUILD_VERSION=dev
|
||||
|
||||
# The controller intentionally does not use DOCKER_HOST or ENUXIA_DOCKER_HOST.
|
||||
@@ -0,0 +1,15 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
*.rs text eol=lf
|
||||
*.toml text eol=lf
|
||||
*.md text eol=lf
|
||||
*.yml text eol=lf
|
||||
*.yaml text eol=lf
|
||||
*.sh text eol=lf
|
||||
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.jpeg binary
|
||||
*.gif binary
|
||||
*.webp binary
|
||||
*.zip binary
|
||||
@@ -0,0 +1 @@
|
||||
* @luunz71
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Report reproducible incorrect behavior
|
||||
title: "bug: "
|
||||
labels:
|
||||
- bug
|
||||
---
|
||||
|
||||
## Environment
|
||||
|
||||
- Commit or release:
|
||||
- Linux distribution:
|
||||
- Docker version:
|
||||
- Frappe/ERPNext version:
|
||||
- Deployment layout:
|
||||
|
||||
## Description
|
||||
|
||||
## Reproduction
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
## Expected behavior
|
||||
|
||||
## Observed behavior
|
||||
|
||||
## Logs
|
||||
|
||||
Remove secrets, tokens, personal data, and private application data before posting logs.
|
||||
|
||||
## Security impact
|
||||
|
||||
If this may be a vulnerability, stop and follow `SECURITY.md` instead of publishing the report.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Propose a focused capability
|
||||
title: "feat: "
|
||||
labels:
|
||||
- enhancement
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
## Proposed behavior
|
||||
|
||||
## Security-boundary impact
|
||||
|
||||
Does the change affect the controller, agent, Docker access, protocol, SQLite state, backup storage, or destructive operations?
|
||||
|
||||
## Alternatives
|
||||
|
||||
## Additional context
|
||||
@@ -0,0 +1,22 @@
|
||||
## Summary
|
||||
|
||||
## Why
|
||||
|
||||
## Security-boundary impact
|
||||
|
||||
- [ ] No Docker access was added to the controller
|
||||
- [ ] No arbitrary shell execution was added
|
||||
- [ ] New protocol data is validated and bounded
|
||||
- [ ] State-changing work is persisted
|
||||
- [ ] Tests cover the change
|
||||
|
||||
## Verification
|
||||
|
||||
```text
|
||||
cargo fmt --all -- --check
|
||||
cargo check --workspace
|
||||
cargo test --workspace
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Manual testing
|
||||
@@ -0,0 +1,29 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
rust:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt
|
||||
|
||||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
|
||||
- name: Check workspace
|
||||
run: cargo check --workspace
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --workspace
|
||||
+6
-3
@@ -9,21 +9,24 @@ secrets/
|
||||
*.secret
|
||||
*.key
|
||||
*.pem
|
||||
|
||||
# État d’exécution local
|
||||
*.sock
|
||||
*.pid
|
||||
state/
|
||||
backups/
|
||||
logs/
|
||||
|
||||
# Fichiers temporaires et sauvegardes d’éditeur
|
||||
*.bak
|
||||
*.tmp
|
||||
*.swp
|
||||
*~
|
||||
.DS_Store
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
# Public repository safety
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
*.db
|
||||
*.log
|
||||
/backups/
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Planned
|
||||
|
||||
- Restore workflow
|
||||
- Backup retention
|
||||
- Scheduled backups
|
||||
- Installation and service packaging
|
||||
- Additional hardening and failure-injection tests
|
||||
|
||||
## [0.1.0] - 2026-06-24
|
||||
|
||||
### Added
|
||||
|
||||
- Rust workspace with protocol, agent, and controller components
|
||||
- Typed JSON protocol over a Unix socket
|
||||
- Mode `0600` agent socket
|
||||
- SQLite operation persistence with WAL and restart recovery
|
||||
- Docker Compose discovery through labels
|
||||
- Complete Frappe backup workflow
|
||||
- Backup manifests with sizes and SHA-256 checksums
|
||||
- Backup history exposed through the agent
|
||||
- Read-only backup integrity verification
|
||||
- Typed service status API
|
||||
- Bounded service log API
|
||||
- Axum/Askama dashboard
|
||||
- Unprivileged controller with no Docker or shell access
|
||||
@@ -0,0 +1,9 @@
|
||||
# Code of Conduct
|
||||
|
||||
We want the Enuxia Frappe AIO community to be constructive, technically rigorous, and welcoming.
|
||||
|
||||
Participants are expected to communicate respectfully, critique ideas rather than people, document security concerns responsibly, and respect privacy.
|
||||
|
||||
Harassment, discrimination, threats, deliberate disruption, publication of private information, and premature disclosure of vulnerabilities are not acceptable.
|
||||
|
||||
Maintainers may edit or remove contributions and restrict participation when behavior conflicts with this policy. Reports should be sent privately to the Enuxia organization maintainers.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Contributing
|
||||
|
||||
## Development principles
|
||||
|
||||
Every change must preserve these rules:
|
||||
|
||||
1. The controller remains unprivileged.
|
||||
2. The controller never accesses Docker directly.
|
||||
3. The controller never executes arbitrary system commands.
|
||||
4. Privileged behavior belongs in the local agent.
|
||||
5. Agent operations are typed and explicitly allow-listed.
|
||||
6. Requests and responses are bounded and validated.
|
||||
7. State-changing work is persisted.
|
||||
8. Backup verification remains read-only.
|
||||
9. Docker resources are discovered through labels, not physical names.
|
||||
10. Tests accompany protocol and security-boundary changes.
|
||||
|
||||
## Local verification
|
||||
|
||||
```bash
|
||||
cargo fmt --all -- --check
|
||||
cargo check --workspace
|
||||
cargo test --workspace
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Commit messages
|
||||
|
||||
Use concise Conventional Commit-style messages where practical:
|
||||
|
||||
```text
|
||||
feat: expose bounded service logs through agent
|
||||
fix: reject unsafe backup identifiers
|
||||
refactor: remove Docker access from controller
|
||||
docs: document restore safety model
|
||||
```
|
||||
|
||||
## Protocol changes
|
||||
|
||||
Protocol changes require stable JSON-shape tests, serialization round trips, compatibility consideration, and strict validation.
|
||||
|
||||
Do not add a generic shell-command request.
|
||||
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,4 @@
|
||||
Enuxia Frappe AIO
|
||||
Copyright 2026 Enuxia
|
||||
|
||||
This product is an independent open-source project and is not affiliated with or endorsed by Frappe Technologies.
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
# Enuxia Frappe AIO
|
||||
|
||||
[English version](README.md)
|
||||
|
||||
Contrôleur local, sécurisé et orienté exploitation pour une stack Frappe ou ERPNext.
|
||||
|
||||
> **Aperçu technique — v0.1.0**
|
||||
>
|
||||
> La version actuelle prend en charge l’état des services, les journaux bornés, la création de sauvegardes Frappe, leur historique et leur vérification d’intégrité en lecture seule. La restauration, la rétention, la planification, le packaging et le durcissement de production restent à développer.
|
||||
|
||||
## Pourquoi ce projet
|
||||
|
||||
L’exploitation d’une stack Frappe auto-hébergée demande souvent un accès direct à Docker, des commandes shell et une connaissance précise du déploiement. Enuxia Frappe AIO vise une couche d’exploitation plus sûre :
|
||||
|
||||
- le contrôleur web n’accède pas au socket Docker ;
|
||||
- le contrôleur n’exécute aucune commande système ;
|
||||
- les actions privilégiées sont exposées par des opérations typées et autorisées ;
|
||||
- les opérations et leurs résultats sont persistés dans SQLite ;
|
||||
- les sauvegardes contiennent un manifeste et des empreintes SHA-256 ;
|
||||
- la vérification d’intégrité est réalisée en lecture seule ;
|
||||
- l’état et les journaux des services passent par l’agent local.
|
||||
|
||||
## Fonctionnalités actuelles
|
||||
|
||||
- Agent local Rust avec accès à l’API Docker
|
||||
- Contrôleur Axum/Askama non privilégié
|
||||
- Protocole JSON typé sur socket Unix en mode `0600`
|
||||
- File d’opérations persistée dans SQLite
|
||||
- Sauvegarde complète d’un site Frappe
|
||||
- Historique des sauvegardes
|
||||
- Vérification d’intégrité en lecture seule
|
||||
- Vérification des tailles et SHA-256
|
||||
- État typé des services Frappe attendus
|
||||
- Consultation bornée des journaux
|
||||
- Découverte par labels Docker Compose
|
||||
- Récupération des opérations interrompues après redémarrage de l’agent
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
Navigateur
|
||||
|
|
||||
v
|
||||
enuxia-aio-controller
|
||||
- non privilégié
|
||||
- aucun socket Docker
|
||||
- aucune commande shell
|
||||
|
|
||||
| JSON typé / socket Unix
|
||||
v
|
||||
enuxia-aio-agent
|
||||
- opérations autorisées
|
||||
- état SQLite
|
||||
- accès API Docker
|
||||
|
|
||||
v
|
||||
Conteneurs Frappe et conteneurs temporaires d’opération
|
||||
```
|
||||
|
||||
Consultez [l’architecture](docs/ARCHITECTURE.md) et le [modèle de sécurité](docs/SECURITY-MODEL.md).
|
||||
|
||||
## État du projet
|
||||
|
||||
| Fonction | État |
|
||||
|---|---|
|
||||
| État des services | Disponible |
|
||||
| Journaux bornés | Disponible |
|
||||
| Création de sauvegarde | Disponible |
|
||||
| Historique | Disponible |
|
||||
| Vérification d’intégrité | Disponible |
|
||||
| Restauration | Planifiée |
|
||||
| Politique de rétention | Planifiée |
|
||||
| Sauvegardes planifiées | Planifiées |
|
||||
| Packaging | Planifié |
|
||||
| Audit de sécurité de production | Non réalisé |
|
||||
|
||||
## Démarrage en développement
|
||||
|
||||
```bash
|
||||
cargo fmt --all -- --check
|
||||
cargo check --workspace
|
||||
cargo test --workspace
|
||||
```
|
||||
|
||||
### Agent
|
||||
|
||||
```bash
|
||||
export ENUXIA_AGENT_SOCKET="${XDG_RUNTIME_DIR}/enuxia-frappe-aio/agent.sock"
|
||||
export ENUXIA_AGENT_STATE_DB="${XDG_STATE_HOME:-$HOME/.local/state}/enuxia-frappe-aio/agent.sqlite3"
|
||||
export ENUXIA_DOCKER_HOST="unix:///var/run/docker.sock"
|
||||
export ENUXIA_COMPOSE_PROJECT="mon-projet-frappe"
|
||||
export ENUXIA_SITE_NAME="site.example.test"
|
||||
|
||||
cargo run -p enuxia-aio-agent
|
||||
```
|
||||
|
||||
### Contrôleur
|
||||
|
||||
```bash
|
||||
unset ENUXIA_DOCKER_HOST
|
||||
unset DOCKER_HOST
|
||||
|
||||
export ENUXIA_AGENT_SOCKET="${XDG_RUNTIME_DIR}/enuxia-frappe-aio/agent.sock"
|
||||
export ENUXIA_COMPOSE_PROJECT="mon-projet-frappe"
|
||||
export ENUXIA_INSTANCE_NAME="mon-instance-frappe"
|
||||
export ENUXIA_SITE_NAME="site.example.test"
|
||||
export ENUXIA_TARGET_NAME="nœud-local"
|
||||
|
||||
cargo run -p enuxia-aio-controller
|
||||
```
|
||||
|
||||
Ouvrir `http://127.0.0.1:3000`.
|
||||
|
||||
## Sécurité
|
||||
|
||||
Lire [SECURITY.md](SECURITY.md) avant de signaler une vulnérabilité.
|
||||
|
||||
Contraintes principales :
|
||||
|
||||
- ne jamais exposer le socket de l’agent sur TCP ;
|
||||
- ne jamais exposer le socket Docker au contrôleur ;
|
||||
- ne jamais ajouter d’exécution shell arbitraire au protocole ;
|
||||
- considérer l’agent comme un composant local privilégié ;
|
||||
- ne pas utiliser cet aperçu technique comme unique mécanisme de sauvegarde.
|
||||
|
||||
## Licence
|
||||
|
||||
Apache License 2.0. Voir [LICENSE](LICENSE).
|
||||
|
||||
## Avertissement
|
||||
|
||||
Ce projet open source indépendant est développé par Enuxia. Il n’est ni affilié ni approuvé par Frappe Technologies.
|
||||
@@ -0,0 +1,177 @@
|
||||
# Enuxia Frappe AIO
|
||||
|
||||
[Version française](README.fr.md)
|
||||
|
||||
A local, security-oriented controller for operating a Frappe or ERPNext stack.
|
||||
|
||||
> **Technical Preview — v0.1.0**
|
||||
>
|
||||
> The current release supports service monitoring, bounded logs, Frappe backups, backup history, and read-only integrity verification. Restore, retention, scheduling, packaging, and production hardening are still in progress.
|
||||
|
||||
## Why this project exists
|
||||
|
||||
Operating a self-hosted Frappe stack often requires direct Docker access, shell commands, and knowledge of the deployment layout. Enuxia Frappe AIO introduces a safer operational layer:
|
||||
|
||||
- the web controller has no Docker socket access;
|
||||
- the controller executes no system commands;
|
||||
- privileged actions are exposed as typed, allow-listed agent operations;
|
||||
- operations and results are persisted in SQLite;
|
||||
- backups include a manifest and SHA-256 checksums;
|
||||
- integrity verification runs read-only;
|
||||
- service state and logs are obtained through the local agent.
|
||||
|
||||
## Current capabilities
|
||||
|
||||
- Local Rust agent with Docker API access
|
||||
- Unprivileged Axum/Askama controller
|
||||
- Typed JSON protocol over a mode `0600` Unix socket
|
||||
- Persistent operation queue and results in SQLite
|
||||
- Complete Frappe backup workflow
|
||||
- Four backup artifact roles: database, configuration, public files, private files
|
||||
- Backup history
|
||||
- Read-only integrity verification
|
||||
- Size and SHA-256 verification for every artifact
|
||||
- Typed status for the expected Frappe services
|
||||
- Bounded service log retrieval
|
||||
- Docker Compose discovery through labels
|
||||
- Recovery of interrupted operations after agent restart
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
Browser
|
||||
|
|
||||
v
|
||||
enuxia-aio-controller
|
||||
- unprivileged
|
||||
- no Docker socket
|
||||
- no shell execution
|
||||
|
|
||||
| typed JSON / Unix socket
|
||||
v
|
||||
enuxia-aio-agent
|
||||
- allow-listed operations
|
||||
- SQLite state
|
||||
- Docker API access
|
||||
|
|
||||
v
|
||||
Frappe containers and temporary operation containers
|
||||
```
|
||||
|
||||
See [Architecture](docs/ARCHITECTURE.md) and [Security model](docs/SECURITY-MODEL.md).
|
||||
|
||||
## Project status
|
||||
|
||||
| Capability | Status |
|
||||
|---|---|
|
||||
| Service status | Available |
|
||||
| Bounded service logs | Available |
|
||||
| Create backup | Available |
|
||||
| Backup history | Available |
|
||||
| Verify backup integrity | Available |
|
||||
| Restore backup | Planned |
|
||||
| Retention policy | Planned |
|
||||
| Scheduled backups | Planned |
|
||||
| Installation packaging | Planned |
|
||||
| Production security audit | Not completed |
|
||||
|
||||
## Requirements
|
||||
|
||||
- Linux
|
||||
- Rust stable toolchain
|
||||
- Docker Engine
|
||||
- A Docker Compose-managed Frappe stack
|
||||
- A dedicated local Unix socket for controller-to-agent communication
|
||||
|
||||
The agent requires Docker access. Treat access to a Docker socket as highly privileged.
|
||||
|
||||
## Development quick start
|
||||
|
||||
```bash
|
||||
cargo fmt --all -- --check
|
||||
cargo check --workspace
|
||||
cargo test --workspace
|
||||
```
|
||||
|
||||
### Start the agent
|
||||
|
||||
```bash
|
||||
export ENUXIA_AGENT_SOCKET="${XDG_RUNTIME_DIR}/enuxia-frappe-aio/agent.sock"
|
||||
export ENUXIA_AGENT_STATE_DB="${XDG_STATE_HOME:-$HOME/.local/state}/enuxia-frappe-aio/agent.sqlite3"
|
||||
export ENUXIA_DOCKER_HOST="unix:///var/run/docker.sock"
|
||||
export ENUXIA_COMPOSE_PROJECT="my-frappe-project"
|
||||
export ENUXIA_SITE_NAME="site.example.test"
|
||||
|
||||
cargo run -p enuxia-aio-agent
|
||||
```
|
||||
|
||||
### Start the controller
|
||||
|
||||
```bash
|
||||
unset ENUXIA_DOCKER_HOST
|
||||
unset DOCKER_HOST
|
||||
|
||||
export ENUXIA_AGENT_SOCKET="${XDG_RUNTIME_DIR}/enuxia-frappe-aio/agent.sock"
|
||||
export ENUXIA_COMPOSE_PROJECT="my-frappe-project"
|
||||
export ENUXIA_INSTANCE_NAME="my-frappe-instance"
|
||||
export ENUXIA_SITE_NAME="site.example.test"
|
||||
export ENUXIA_TARGET_NAME="local-node"
|
||||
|
||||
cargo run -p enuxia-aio-controller
|
||||
```
|
||||
|
||||
Open `http://127.0.0.1:3000`.
|
||||
|
||||
Additional variables are documented in [.env.example](.env.example).
|
||||
|
||||
## Expected Compose services
|
||||
|
||||
The current dashboard expects these Compose service labels:
|
||||
|
||||
- `frontend`
|
||||
- `backend`
|
||||
- `db`
|
||||
- `redis-cache`
|
||||
- `redis-queue`
|
||||
- `websocket`
|
||||
- `queue-short`
|
||||
- `queue-long`
|
||||
- `scheduler`
|
||||
|
||||
Physical container names are not part of the public contract.
|
||||
|
||||
## Backup model
|
||||
|
||||
Each successful backup records a unique identifier, site, timestamp, total size, storage location, four artifacts, and one SHA-256 checksum per artifact.
|
||||
|
||||
Integrity verification reloads the persisted manifest, mounts backup storage read-only, recomputes sizes and checksums, and persists a structured verification result.
|
||||
|
||||
This is not a replacement for off-site backups, restore testing, or a disaster-recovery plan.
|
||||
|
||||
## Security
|
||||
|
||||
Read [SECURITY.md](SECURITY.md) before reporting a vulnerability.
|
||||
|
||||
Important constraints:
|
||||
|
||||
- never expose the agent socket over TCP;
|
||||
- never expose the Docker socket to the controller;
|
||||
- never add arbitrary shell execution to the protocol;
|
||||
- treat the agent as a privileged local component;
|
||||
- do not use this technical preview as the only backup mechanism.
|
||||
|
||||
## Contributing
|
||||
|
||||
Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a pull request.
|
||||
|
||||
## Roadmap
|
||||
|
||||
See [ROADMAP.md](ROADMAP.md).
|
||||
|
||||
## License
|
||||
|
||||
Apache License 2.0. See [LICENSE](LICENSE).
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This is an independent open-source project by Enuxia. It is not affiliated with or endorsed by Frappe Technologies.
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
# Roadmap
|
||||
|
||||
This roadmap describes intent, not a delivery commitment.
|
||||
|
||||
## v0.2 — Restore V1
|
||||
|
||||
- Select only a valid, manifested backup
|
||||
- Explicit destructive-action confirmation
|
||||
- Persisted restore operation
|
||||
- Pre-restore safety checks
|
||||
- Maintenance-mode handling
|
||||
- Database and file restoration
|
||||
- Post-restore migration and health checks
|
||||
- Failure reporting and recovery plan
|
||||
- Real restore tests on disposable environments
|
||||
|
||||
## v0.3 — Backup operations
|
||||
|
||||
- Retention policies
|
||||
- Manual deletion
|
||||
- Scheduled backups
|
||||
- Automatic post-backup verification
|
||||
- Storage-capacity checks
|
||||
- Off-site export interface
|
||||
|
||||
## v0.4 — Packaging
|
||||
|
||||
- systemd units
|
||||
- Dedicated users and groups
|
||||
- Installation and upgrade tooling
|
||||
- Configuration validation
|
||||
- Release artifacts
|
||||
|
||||
## v1.0 — Stable foundation
|
||||
|
||||
- Security review
|
||||
- Restore workflow validated
|
||||
- Upgrade and rollback documentation
|
||||
- Stable protocol policy
|
||||
- Production deployment guide
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Security Policy
|
||||
|
||||
## Project status
|
||||
|
||||
Enuxia Frappe AIO is a technical preview. It has not received a formal independent security audit and must not be treated as a complete production backup or disaster-recovery solution.
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Do not open a public issue for a suspected vulnerability.
|
||||
|
||||
Use a private communication channel with the Enuxia organization maintainers on the Gitea instance. Include the affected commit, reproduction steps, impact, and any proposed mitigation.
|
||||
|
||||
## Security boundaries
|
||||
|
||||
### Controller
|
||||
|
||||
The controller must not:
|
||||
|
||||
- access the Docker socket;
|
||||
- execute system commands;
|
||||
- read the agent SQLite database directly;
|
||||
- expose privileged operations outside the typed protocol.
|
||||
|
||||
### Agent
|
||||
|
||||
The agent is privileged because it can access Docker. It must:
|
||||
|
||||
- remain local;
|
||||
- expose only allow-listed typed operations;
|
||||
- use a mode `0600` Unix socket;
|
||||
- validate every identifier and bound;
|
||||
- avoid arbitrary command execution;
|
||||
- keep responses bounded;
|
||||
- persist state-changing operations safely.
|
||||
|
||||
## Operational guidance
|
||||
|
||||
- Restrict access to the agent socket.
|
||||
- Do not bind the agent protocol to TCP.
|
||||
- Protect the SQLite state database.
|
||||
- Use off-site backups.
|
||||
- Test restoration on disposable infrastructure.
|
||||
- Do not rely solely on checksum verification.
|
||||
- Review configuration before exposing the web interface beyond localhost.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
```text
|
||||
Browser
|
||||
|
|
||||
v
|
||||
Controller
|
||||
|
|
||||
| typed JSON / Unix socket 0600
|
||||
v
|
||||
Agent
|
||||
|\
|
||||
| \__ SQLite operation state
|
||||
|
|
||||
\____ Docker API
|
||||
|
|
||||
+-- Frappe Compose services
|
||||
+-- temporary operation containers
|
||||
```
|
||||
|
||||
## Controller
|
||||
|
||||
The controller displays agent health, service state, logs, backup history, and operation results. It has no Docker access and no direct SQLite access.
|
||||
|
||||
## Agent
|
||||
|
||||
The agent validates typed requests, owns Docker access, discovers resources through Compose labels, persists operations, runs the worker, and returns bounded structured responses.
|
||||
|
||||
## Worker
|
||||
|
||||
The worker atomically claims queued operations, marks them running, executes an allow-listed operation, and persists a result or error. Interrupted running operations are marked failed after restart.
|
||||
|
||||
## Backup workflow
|
||||
|
||||
```text
|
||||
Controller -> StartOperation(Backup)
|
||||
Agent -> persist queued operation
|
||||
Worker -> discover runtime -> run backup -> hash artifacts -> persist BackupResult
|
||||
Controller -> poll status -> display result and history
|
||||
```
|
||||
|
||||
## Verification workflow
|
||||
|
||||
```text
|
||||
Controller -> StartOperation(VerifyBackup)
|
||||
Agent -> load persisted manifest
|
||||
Worker -> mount storage read-only -> recompute sizes and SHA-256 -> persist result
|
||||
Controller -> display global and per-artifact result
|
||||
```
|
||||
|
||||
## Discovery contract
|
||||
|
||||
The implementation relies on Docker Compose labels such as `com.docker.compose.project`, `com.docker.compose.service`, and `com.docker.compose.volume`.
|
||||
|
||||
Physical container names are display information, not stable identifiers.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Security Model
|
||||
|
||||
## Trust boundaries
|
||||
|
||||
### Browser to controller
|
||||
|
||||
The web interface is currently intended for local or otherwise protected access. Authentication and reverse-proxy hardening are not yet provided by the technical preview.
|
||||
|
||||
### Controller to agent
|
||||
|
||||
Controls include filesystem permissions, typed requests, protocol-version validation, strict identifier validation, bounded responses, and no generic command request.
|
||||
|
||||
### Agent to Docker
|
||||
|
||||
This is the strongest privilege boundary. Docker access can provide effective control over the host. Compromise of the agent must be treated as severe.
|
||||
|
||||
## Core invariants
|
||||
|
||||
- The controller has no Docker socket.
|
||||
- The controller has no shell-execution facility.
|
||||
- The controller does not read SQLite directly.
|
||||
- The agent accepts only known request variants.
|
||||
- Operation parameters are validated.
|
||||
- Backup identifiers cannot contain path traversal.
|
||||
- Service identifiers are restricted.
|
||||
- Verification storage is mounted read-only.
|
||||
- Backup artifacts must be regular files during verification.
|
||||
- Service logs are bounded.
|
||||
- State-changing operations are persisted.
|
||||
|
||||
## Integrity limitations
|
||||
|
||||
Checksums detect unexpected artifact changes. They do not prove semantic database validity, successful restoration, off-site durability, or the absence of coordinated tampering with both data and metadata.
|
||||
|
||||
Regular restoration tests remain necessary.
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -n "$(git status --porcelain)" ]]; then
|
||||
echo "The working tree is not clean." >&2
|
||||
git status --short
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cargo fmt --all -- --check
|
||||
cargo check --workspace
|
||||
cargo test --workspace
|
||||
git diff --check
|
||||
|
||||
if git ls-files | grep -Ei '(^|/)(\.env($|\.)|.*\.pem$|.*\.key$|id_rsa$|id_ed25519$|.*\.sqlite3?$|.*\.db$)'; then
|
||||
echo "Potentially sensitive tracked file found." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Public release checks passed."
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
cargo fmt --all -- --check
|
||||
cargo check --workspace
|
||||
cargo test --workspace
|
||||
git diff --check
|
||||
Reference in New Issue
Block a user