mirror of
https://github.com/garethgeorge/backrest.git
synced 2026-08-26 10:56:33 +00:00
some side work to enable nix dev shells with dev deps
This commit is contained in:
+14
-14
@@ -1,19 +1,19 @@
|
||||
FROM mcr.microsoft.com/devcontainers/base:bookworm
|
||||
|
||||
# Install Nix in single-user mode
|
||||
RUN curl -L https://nixos.org/nix/install | bash -s -- --no-daemon \
|
||||
&& echo '. /root/.nix-profile/etc/profile.d/nix.sh' >> /etc/bash.bashrc
|
||||
# Install Nix in single-user mode for the vscode user
|
||||
USER vscode
|
||||
RUN curl -L https://nixos.org/nix/install | bash -s -- --no-daemon
|
||||
ENV PATH="/home/vscode/.nix-profile/bin:${PATH}"
|
||||
|
||||
ENV PATH="/root/.nix-profile/bin:${PATH}"
|
||||
# Install direnv via Nix and configure shell hooks
|
||||
RUN . /home/vscode/.nix-profile/etc/profile.d/nix.sh \
|
||||
&& nix-env -iA nixpkgs.direnv
|
||||
RUN echo '. /home/vscode/.nix-profile/etc/profile.d/nix.sh' >> /home/vscode/.bashrc \
|
||||
&& echo 'eval "$(direnv hook bash)"' >> /home/vscode/.bashrc \
|
||||
&& echo '. /home/vscode/.nix-profile/etc/profile.d/nix.sh' >> /home/vscode/.zshrc \
|
||||
&& echo 'eval "$(direnv hook zsh)"' >> /home/vscode/.zshrc
|
||||
|
||||
# Install direnv via Nix and configure the shell hook
|
||||
RUN . /root/.nix-profile/etc/profile.d/nix.sh \
|
||||
&& nix-env -iA nixpkgs.direnv \
|
||||
&& echo 'eval "$(direnv hook bash)"' >> /etc/bash.bashrc \
|
||||
&& echo 'eval "$(direnv hook zsh)"' >> /etc/zshrc
|
||||
|
||||
# Pre-populate the nix store with project dependencies
|
||||
# Pre-populate the nix store with project dependencies so first open is fast
|
||||
COPY shell.nix /tmp/shell.nix
|
||||
RUN . /root/.nix-profile/etc/profile.d/nix.sh \
|
||||
&& nix-shell /tmp/shell.nix --run "echo 'nix dependencies cached'" \
|
||||
&& rm /tmp/shell.nix
|
||||
RUN . /home/vscode/.nix-profile/etc/profile.d/nix.sh \
|
||||
&& nix-shell /tmp/shell.nix --run "echo 'nix dependencies cached'"
|
||||
|
||||
@@ -1,49 +1,29 @@
|
||||
{
|
||||
"name": "Go",
|
||||
"name": "Backrest Dev",
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile",
|
||||
"args": {
|
||||
// Update the VARIANT arg to pick a version of Go: 1, 1.16, 1.17
|
||||
// Append -bullseye or -buster to pin to an OS version.
|
||||
// Use -bullseye variants on local arm64/Apple Silicon.
|
||||
"VARIANT": "1-1.24-bookworm",
|
||||
// Options
|
||||
"NODE_VERSION": "lts/*"
|
||||
}
|
||||
"context": ".."
|
||||
},
|
||||
"runArgs": [
|
||||
"--cap-add=SYS_PTRACE",
|
||||
"--security-opt",
|
||||
"seccomp=unconfined"
|
||||
],
|
||||
"postCreateCommand": "direnv allow",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
// Set *default* container specific settings.json values on container create.
|
||||
"settings": {
|
||||
"go.toolsManagement.checkForUpdates": "local",
|
||||
"go.useLanguageServer": true,
|
||||
"go.gopath": "/go",
|
||||
"go.goroot": "/usr/local/go",
|
||||
"typescript.tsdk": "webui/node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"gitlens.telemetry.enabled": false
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
},
|
||||
// Add the IDs of extensions you want installed when the container is created.
|
||||
"extensions": [
|
||||
"golang.Go",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"mhutchie.git-graph",
|
||||
"eamodio.gitlens",
|
||||
"donjayamanne.githistory",
|
||||
"esbenp.prettier-vscode",
|
||||
"iulian-radu-at.vscode-tasks-sidebar"
|
||||
"mkhl.direnv",
|
||||
"esbenp.prettier-vscode"
|
||||
]
|
||||
}
|
||||
},
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "go version",
|
||||
// Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root.
|
||||
"remoteUser": "vscode"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Nix
|
||||
result
|
||||
|
||||
/backrest
|
||||
/backrest-*
|
||||
dist
|
||||
|
||||
@@ -251,17 +251,29 @@ Contributions are welcome! See the [issues](https://github.com/garethgeorge/back
|
||||
|
||||
## Build Dependencies
|
||||
|
||||
- [Node.js](https://nodejs.org/en) for UI development
|
||||
- [Go](https://go.dev/) 1.21 or greater for server development
|
||||
All build dependencies are defined in `shell.nix` and can be activated automatically using [Nix](https://nixos.org/) and [direnv](https://direnv.net/).
|
||||
|
||||
### Using Nix + direnv (Recommended)
|
||||
|
||||
1. Install [Nix](https://nixos.org/download/) and [direnv](https://direnv.net/docs/installation.html)
|
||||
2. [Hook direnv into your shell](https://direnv.net/docs/hook.html) (e.g. `eval "$(direnv hook bash)"` in your `.bashrc`)
|
||||
3. Clone the repo and `cd` into it
|
||||
4. Run `direnv allow` to trust the `.envrc` — all dependencies (Go, Node.js, pnpm, protoc, buf, etc.) will be available in your shell automatically
|
||||
|
||||
### Manual Setup
|
||||
|
||||
If you prefer not to use Nix, install the following manually:
|
||||
|
||||
- [Go](https://go.dev/) 1.24 or greater
|
||||
- [Node.js](https://nodejs.org/en) 20.x and [pnpm](https://pnpm.io/) 9
|
||||
- [goreleaser](https://github.com/goreleaser/goreleaser) `go install github.com/goreleaser/goreleaser@latest`
|
||||
|
||||
**(Optional) To Edit Protobuffers**
|
||||
**(Optional) To edit protobuf definitions:**
|
||||
|
||||
```sh
|
||||
apt install -y protobuf-compiler
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
|
||||
go install github.com/bufbuild/buf/cmd/buf@v1.47.2
|
||||
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
|
||||
go install github.com/bufbuild/buf/cmd/buf@latest
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
|
||||
go install connectrpc.com/connect/cmd/protoc-gen-connect-go@latest
|
||||
npm install -g @bufbuild/protoc-gen-es
|
||||
@@ -270,22 +282,22 @@ npm install -g @bufbuild/protoc-gen-es
|
||||
## Compiling
|
||||
|
||||
```sh
|
||||
(cd webui && npm i && npm run build)
|
||||
(cd webui && pnpm i && pnpm run build)
|
||||
(cd cmd/backrest && go build .)
|
||||
```
|
||||
|
||||
## Using VSCode Dev Containers
|
||||
|
||||
You can also use VSCode with [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension to quickly get up and running with a working development and debugging environment.
|
||||
The dev container uses Nix and direnv to provide all dependencies. When the container starts, `direnv allow` runs automatically so the Nix shell is activated in every terminal.
|
||||
|
||||
0. Make sure Docker and VSCode with [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension is installed
|
||||
0. Make sure Docker and VSCode with the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension is installed
|
||||
1. Clone this repository
|
||||
2. Open this folder in VSCode
|
||||
3. When prompted, click on `Open in Container` button, or run `> Dev Containers: Rebuild and Reopen in Containers` command
|
||||
4. When container is started, go to `Run and Debug`, choose `Debug Backrest (backend+frontend)` and run it
|
||||
4. When the container is started, go to `Run and Debug`, choose `Debug Backrest (backend+frontend)` and run it
|
||||
|
||||
> [!NOTE]
|
||||
> Provided launch configuration has hot reload for typescript frontend.
|
||||
> Provided launch configuration has hot reload for the typescript frontend.
|
||||
|
||||
## Translations
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ export default defineConfig({
|
||||
items: [
|
||||
{ text: 'Operations', link: '/docs/operations' },
|
||||
{ text: 'Hooks', link: '/docs/hooks' },
|
||||
{ text: 'Multihost Sync', link: '/docs/multihost' },
|
||||
{ text: 'API', link: '/docs/api' }
|
||||
]
|
||||
},
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
# Multihost Sync
|
||||
|
||||
Backrest supports syncing configuration and operation history between multiple instances. This allows you to monitor and manage backups across several machines from a single Backrest installation.
|
||||
|
||||
## Concepts
|
||||
|
||||
**Server**: A Backrest instance that accepts connections from other instances. The server can push shared repos to clients and receive operation updates from them.
|
||||
|
||||
**Client**: A Backrest instance that connects to a server. The client sends its backup operation history to the server and can receive shared repo configurations.
|
||||
|
||||
**Pairing Token**: A one-time token generated by the server that allows a client to authenticate and register itself as an authorized client.
|
||||
|
||||
**Shared Repo**: A repository on the server marked as "shared." Its configuration is automatically pushed to all connected clients that have the `Receive Shared Repos` permission.
|
||||
|
||||
## Setup Overview
|
||||
|
||||
The typical setup flow is:
|
||||
|
||||
1. Configure both instances with unique **Instance IDs** (Settings > General)
|
||||
2. On the server, generate a **pairing token** with the desired permissions
|
||||
3. On the client, add the server as a **known host** using the pairing token
|
||||
4. The client connects and is automatically registered as an authorized client on the server
|
||||
|
||||
## Step 1: Generate a Pairing Token (Server)
|
||||
|
||||
1. Open **Settings** on the server instance
|
||||
2. Scroll to the **Multihost** section
|
||||
3. Under **Pairing Tokens**, click **Generate**
|
||||
4. Configure the token:
|
||||
- **Label**: A human-readable name (e.g. "laptop-backup")
|
||||
- **TTL**: How long the token is valid (e.g. 15 minutes, 1 hour, or forever)
|
||||
- **Max Uses**: How many clients can pair with this token (0 = unlimited)
|
||||
- **Permissions**: What the paired client will be allowed to do (see [Permissions](#permissions) below)
|
||||
5. Copy the generated token string — it will look like `<keyid>:<secret>#<instanceid>`
|
||||
|
||||
::: tip
|
||||
Pairing tokens are consumed on use. Once a client has paired, the token is no longer needed. Generate short-lived tokens with limited uses for better security.
|
||||
:::
|
||||
|
||||
## Step 2: Connect the Client
|
||||
|
||||
1. Open **Settings** on the client instance
|
||||
2. Scroll to the **Multihost** section
|
||||
3. Under **Known Hosts**, click **Add**
|
||||
4. Paste the **pairing token** from the server
|
||||
5. Enter the server's **Instance URL** (e.g. `https://backrest.example.com:9898`)
|
||||
6. Save the configuration
|
||||
|
||||
The client will connect to the server, present the pairing secret, and be automatically registered as an authorized client. After pairing, the token is cleared and authentication uses public key identity going forward.
|
||||
|
||||
## Permissions
|
||||
|
||||
Permissions control what a connected client can do. They are configured when generating a pairing token or by editing an authorized client's permissions after pairing.
|
||||
|
||||
| Permission | Description |
|
||||
|---|---|
|
||||
| **Read Operations** | The client sends its operation history (backup results, errors, etc.) to the server. This enables centralized monitoring. |
|
||||
| **Read Config** | The client can read repo and plan configuration from the server. |
|
||||
| **Read/Write Config** | The client can read and write repo and plan configuration on the server. |
|
||||
| **Receive Shared Repos** | The server automatically pushes all repos marked as "shared" to this client. |
|
||||
|
||||
### Scopes
|
||||
|
||||
Permissions (except Receive Shared Repos) can be scoped to specific repos or plans:
|
||||
|
||||
- **All** (`*`): Applies to all repos and plans
|
||||
- **Specific repo**: `repo:<repo_id>` — applies only to the named repo
|
||||
- **Specific plan**: `plan:<plan_id>` — applies only to the named plan
|
||||
- **Exclude repo**: `!repo:<repo_id>` — applies to all except the named repo
|
||||
- **Exclude plan**: `!plan:<plan_id>` — applies to all except the named plan
|
||||
|
||||
## Shared Repos
|
||||
|
||||
Marking a repo as "shared" on the server causes its configuration to be automatically pushed to all authorized clients with the `Receive Shared Repos` permission.
|
||||
|
||||
To share a repo:
|
||||
|
||||
1. Open the repo's settings on the server
|
||||
2. Enable the **Shared** toggle
|
||||
3. Save
|
||||
|
||||
When a shared repo is received by a client:
|
||||
|
||||
- The repo appears in the client's repo list with its `originInstanceId` set to the server's instance ID
|
||||
- The repo is **read-only** on the client — it cannot be edited, only deleted
|
||||
- **Scheduling of maintenance tasks (prune, check, forget) is skipped** for shared repos on the client — the server that owns the repo manages these operations
|
||||
- The client can still run backups to the shared repo if it has plans configured for it
|
||||
|
||||
::: warning
|
||||
Shared repos are identified by their GUID. If the client already has a local repo with the same GUID, the shared repo will be skipped to avoid conflicts.
|
||||
:::
|
||||
|
||||
## Monitoring Sync Status
|
||||
|
||||
After setup, the server's Settings page shows the connection status of each authorized client. A green indicator means the client is currently connected and syncing.
|
||||
|
||||
The sync protocol uses a persistent bidirectional connection with:
|
||||
|
||||
- **Heartbeats** to detect disconnections
|
||||
- **Automatic reconnection** with exponential backoff if the connection drops
|
||||
- **Manifest-based reconciliation** to efficiently sync only changed operations
|
||||
|
||||
## Typical Configurations
|
||||
|
||||
### Centralized Monitoring
|
||||
|
||||
Push backup status from multiple machines to a single dashboard:
|
||||
|
||||
- **Server permissions**: `Read Operations` scoped to `*`
|
||||
- **Shared repos**: Not needed — each client manages its own repos
|
||||
- **Result**: Server's operation log shows backup results from all clients
|
||||
|
||||
### Shared Repository
|
||||
|
||||
Multiple machines back up to the same repository, managed by the server:
|
||||
|
||||
- **Server permissions**: `Receive Shared Repos` + `Read Operations` scoped to `*`
|
||||
- **Server repo**: Mark the target repo as **Shared**
|
||||
- **Result**: Clients receive the repo config automatically. The server handles forget, prune, and check. Clients run their own backup plans against the shared repo.
|
||||
|
||||
### Full Config Management
|
||||
|
||||
Server manages configuration for all clients:
|
||||
|
||||
- **Server permissions**: `Read/Write Config` scoped to `*` + `Receive Shared Repos`
|
||||
- **Result**: The server can push config changes (repos and plans) to connected clients
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Client can't connect**: Verify the Instance URL is reachable from the client. The URL should include the port (default 9898). If using a reverse proxy, ensure it supports HTTP/2 and WebSocket connections (needed for the bidirectional sync stream).
|
||||
|
||||
**Pairing fails**: Check that the pairing token hasn't expired and hasn't exceeded its max uses. Generate a new token if needed.
|
||||
|
||||
**Shared repo not appearing on client**: Verify the client has the `Receive Shared Repos` permission. Check that the repo is marked as "shared" on the server. If the client already has a repo with the same GUID, the shared repo will be skipped.
|
||||
|
||||
**Stale snapshots on client**: When the server runs forget and removes snapshots, the client discovers this the next time it indexes snapshots for that repo (typically after a backup). If the client doesn't run backups to a shared repo, its snapshot list may show snapshots that the server has already forgotten until the next index operation.
|
||||
Generated
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1776329215,
|
||||
"narHash": "sha256-a8BYi3mzoJ/AcJP8UldOx8emoPRLeWqALZWu4ZvjPXw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b86751bc4085f48661017fa226dee99fab6c651b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
description = "Backrest development environment";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
||||
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||
in
|
||||
{
|
||||
devShells = forAllSystems (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
# Go backend
|
||||
go_1_24 # go 1.25 not yet in nixpkgs; use latest available
|
||||
goreleaser
|
||||
|
||||
# Frontend
|
||||
nodejs_20
|
||||
pnpm_9
|
||||
|
||||
# Protobuf / code generation
|
||||
protobuf
|
||||
buf
|
||||
protoc-gen-go
|
||||
protoc-gen-go-grpc
|
||||
|
||||
# General build tools
|
||||
gnumake
|
||||
git
|
||||
|
||||
# Runtime dependencies (for local testing)
|
||||
restic
|
||||
rclone
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
echo "backrest dev shell"
|
||||
echo " go : $(go version)"
|
||||
echo " node : $(node --version)"
|
||||
echo " pnpm : $(pnpm --version)"
|
||||
echo " protoc : $(protoc --version)"
|
||||
echo " buf : $(buf --version)"
|
||||
'';
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -1,36 +1,3 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
# Go backend
|
||||
go_1_24 # go 1.25 not yet in nixpkgs; use latest available
|
||||
goreleaser
|
||||
|
||||
# Frontend
|
||||
nodejs_20
|
||||
pnpm_9
|
||||
|
||||
# Protobuf / code generation
|
||||
protobuf
|
||||
buf
|
||||
protoc-gen-go
|
||||
protoc-gen-go-grpc
|
||||
|
||||
# General build tools
|
||||
gnumake
|
||||
git
|
||||
|
||||
# Runtime dependencies (for local testing)
|
||||
restic
|
||||
rclone
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
echo "backrest dev shell"
|
||||
echo " go : $(go version)"
|
||||
echo " node : $(node --version)"
|
||||
echo " pnpm : $(pnpm --version)"
|
||||
echo " protoc : $(protoc --version)"
|
||||
echo " buf : $(buf --version)"
|
||||
'';
|
||||
}
|
||||
# Backward-compatible wrapper for users without flakes enabled.
|
||||
# The canonical definition lives in flake.nix.
|
||||
(builtins.getFlake (toString ./.)).devShells.${builtins.currentSystem}.default
|
||||
|
||||
Reference in New Issue
Block a user