* Getting environment variables and passwords from Vault (not tested
yet)
* Vault configuration to config (not tested yet)
* Ability to run scripts from file on local machine on the remote host
* Ability to get ouput in the notification of a list for individual
commands or all commands
* Make SSH connections close after all commands have been run; reuse
previous connections if needed
This commit is contained in:
Andrew Woodlee 2023-07-01 21:46:54 -05:00
parent 5e7c52997c
commit 4b382bddd9
831 changed files with 83782 additions and 107 deletions

8
.changes/v0.3.0.md Normal file
View File

@ -0,0 +1,8 @@
## v0.3.0 - 2023-01-07
### Added
* Getting environment variables and passwords from Vault (not tested yet)
* Vault configuration to config (not tested yet)
* Ability to run scripts from file on local machine on the remote host
* Ability to get ouput in the notification of a list for individual commands or all commands
### Changed
* Make SSH connections close after all commands have been run; reuse previous connections if needed

View File

@ -30,7 +30,7 @@ jobs:
uses: dawidd6/action-get-tag@v1
with:
# Optionally strip `v` prefix
strip_v: true
strip_v: false
- uses: goreleaser/goreleaser-action@v4
with:
distribution: goreleaser
@ -38,6 +38,3 @@ jobs:
args: release --release-notes=".changes/${{steps.tag.outputs.tag}}.md" -f .goreleaser/github.yml --clean
env:
GITHUB_TOKEN: ${{ secrets.GORELEASER_TOKEN }}
# Your GoReleaser Pro key, if you are using the 'goreleaser-pro'
# distribution:
# GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "docs/themes/hugo-theme-relearn"]
path = docs/themes/hugo-theme-relearn
url = https://github.com/McShelby/hugo-theme-relearn

View File

@ -1,8 +1,8 @@
pipeline:
steps:
release:
image: goreleaser/goreleaser
commands:
- goreleaser release -f .goreleaser/vern.yml --release-notes=".changes/$(go run backy.go version).md"
- goreleaser release -f .goreleaser/vern.yml --release-notes=".changes/$(go run backy.go version -V).md"
secrets: [ gitea_token ]
when:
event: tag

View File

@ -0,0 +1,30 @@
steps:
build:
image: klakegg/hugo:ext-debian-ci
commands:
- git submodule foreach 'git fetch origin; git checkout $(git describe --tags `git rev-list --tags --max-count=1`);'
- cd docs
- hugo
deploy:
image: codingkoopa/git-rsync-openssh
commands:
- cd docs
- echo "151.101.210.132 deb.debian.org" >> /etc/hosts
- echo "nameserver 1.1.1.1" > /etc/resolv.conf
- mkdir ~/.ssh && chmod -R 700 ~/.ssh
# - apt update -y && apt install openssh-client rsync -y
- echo "$SSH_HOST_KEY" > ~/.ssh/known_hosts
- echo -e '#!/bin/sh\necho "$SSH_PASSPHRASE"' | tr -d '\r' > ~/.ssh/.print_ssh_password
# - cat ~/.ssh/.print_ssh_password
- chmod 700 ~/.ssh/.print_ssh_password
- eval $(ssh-agent -s)
- echo "$SSH_DEPLOY_KEY" | tr -d '\r' | DISPLAY=":0.0" SSH_ASKPASS=~/.ssh/.print_ssh_password setsid ssh-add -
- rsync -atv --delete --progress public/ backy@backy.cybershell.xyz:docs
- rsync -atv --delete --progress vangen/ backy@backy.cybershell.xyz:vangen-go
secrets: [ ssh_host_key, ssh_deploy_key, ssh_passphrase ]
branches: master
when:
path: "docs/*"

3
cmd/.gitignore vendored
View File

@ -1 +1,2 @@
*.yaml
*.yaml
*.yml

View File

@ -12,9 +12,9 @@ import (
var (
backupCmd = &cobra.Command{
Use: "backup [--lists=list1,list2]",
Use: "backup [--lists=list1,list2,... | -l list1, list2,...]",
Short: "Runs commands defined in config file.",
Long: "Backup executes commands defined in config file.\nUse the --lists flag to execute the specified commands. If not specified, all lists will be executed.",
Long: "Backup executes commands defined in config file.\nUse the --lists or -l flag to execute the specified lists. If not flag is not given, all lists will be executed.",
Run: Backup,
}
)
@ -34,7 +34,7 @@ func Backup(cmd *cobra.Command, args []string) {
config := backy.ReadConfig(backyConfOpts)
config.RunBackyConfig("", backyConfOpts)
config.RunListConfig("", backyConfOpts)
for _, host := range config.Hosts {
if host.SshClient != nil {
host.SshClient.Close()

View File

@ -9,7 +9,7 @@ import (
var (
cronCmd = &cobra.Command{
Use: "cron [flags]",
Short: "Runs command lists defined in config file.",
Short: "Starts a scheduler that runs lists defined in config file.",
Long: `Cron starts a scheduler that executes command lists at the time defined in config file.`,
Run: cron,
}
@ -19,6 +19,6 @@ func cron(cmd *cobra.Command, args []string) {
opts := backy.NewOpts(cfgFile, backy.UseCron())
opts.InitConfig()
backy.ReadConfig(opts).Cron()
backy.ReadConfig(opts)
opts.Cron()
}

View File

@ -14,8 +14,8 @@ import (
var (
execCmd = &cobra.Command{
Use: "exec command ...",
Short: "Runs commands defined in config file.",
Long: `Exec executes commands defined in config file.`,
Short: "Runs commands defined in config file in order given.",
Long: `Exec executes commands defined in config file in order given.`,
Run: execute,
}
)

View File

@ -7,29 +7,35 @@ import (
"github.com/spf13/cobra"
)
const versionStr = "0.2.4"
const versionStr = "0.3.0"
var (
versionCmd = &cobra.Command{
Use: "version [flags]",
Short: "Prints the version and exits.",
Short: "Prints the version and exits",
Long: "Prints the version and exits. No arguments just prints the version number only.",
Run: version,
}
numOnly bool
vPre bool
)
func version(cmd *cobra.Command, args []string) {
func init() {
versionCmd.PersistentFlags().BoolVarP(&numOnly, "num", "n", true, "Output the version number only.")
versionCmd.PersistentFlags().BoolVarP(&vPre, "vpre", "V", false, "Output the version with v prefixed.")
}
cmd.PersistentFlags().BoolVarP(&numOnly, "num", "n", true, "Output the version number only.")
cmd.PersistentFlags().BoolVarP(&vPre, "vpre", "V", false, "Output the version with v prefixed.")
func version(cmd *cobra.Command, args []string) {
if numOnly && !vPre {
fmt.Printf("%s\n", versionStr)
} else if vPre {
fmt.Printf("v%s", versionStr)
} else if vPre && !numOnly {
fmt.Printf("v%s\n", versionStr)
} else {
fmt.Printf("Backy version: %s", versionStr)
if vPre && numOnly {
fmt.Println("vpre flag and num flag both detected!")
}
fmt.Printf("Backy version: %s\n", versionStr)
}
os.Exit(0)

View File

@ -0,0 +1 @@
{}

0
docs/.hugo_build.lock Normal file
View File

View File

@ -0,0 +1,6 @@
---
title: "{{ replace .Name "-" " " | title }}"
date: {{ .Date }}
draft: true
---

90
docs/config.toml Normal file
View File

@ -0,0 +1,90 @@
baseURL = 'http://example.org/'
languageCode = 'en-us'
title = 'A tool for commands'
# Change the default theme to be use when building the site with Hugo
theme = "hugo-theme-relearn"
# For search functionality
[outputs]
home = [ "HTML", "RSS", "SEARCH"]
[params]
# This controls whether submenus will be expanded (true), or collapsed (false) in the
# menu; if no setting is given, the first menu level is set to false, all others to true;
# this can be overridden in the pages frontmatter
alwaysopen = false
# Prefix URL to edit current page. Will display an "Edit" button on top right hand corner of every page.
# Useful to give opportunity to people to create merge request for your doc.
# See the config.toml file from this documentation site to have an example.
editURL = ""
# Author of the site, will be used in meta information
author = ""
# Description of the site, will be used in meta information
description = ""
# Shows a checkmark for visited pages on the menu
showVisitedLinks = false
# Disable search function. It will hide search bar
disableSearch = false
# Disable search in hidden pages, otherwise they will be shown in search box
disableSearchHiddenPages = false
# Disables hidden pages from showing up in the sitemap and on Google (et all), otherwise they may be indexed by search engines
disableSeoHiddenPages = false
# Disables hidden pages from showing up on the tags page although the tag term will be displayed even if all pages are hidden
disableTagHiddenPages = false
# Javascript and CSS cache are automatically busted when new version of site is generated.
# Set this to true to disable this behavior (some proxies don't handle well this optimization)
disableAssetsBusting = false
# Set this to true if you want to disable generation for generator version meta tags of hugo and the theme;
# don't forget to also set Hugo's disableHugoGeneratorInject=true, otherwise it will generate a meta tag into your home page
disableGeneratorVersion = false
# Set this to true to disable copy-to-clipboard button for inline code.
disableInlineCopyToClipBoard = false
# A title for shortcuts in menu is set by default. Set this to true to disable it.
disableShortcutsTitle = false
# If set to false, a Home button will appear below the search bar on the menu.
# It is redirecting to the landing page of the current language if specified. (Default is "/")
disableLandingPageButton = true
# When using mulitlingual website, disable the switch language button.
disableLanguageSwitchingButton = false
# Hide breadcrumbs in the header and only show the current page title
disableBreadcrumb = true
# If set to true, hide table of contents menu in the header of all pages
disableToc = false
# If set to false, load the MathJax module on every page regardless if a MathJax shortcode is present
disableMathJax = false
# Specifies the remote location of the MathJax js
customMathJaxURL = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"
# Initialization parameter for MathJax, see MathJax documentation
mathJaxInitialize = "{}"
# If set to false, load the Mermaid module on every page regardless if a Mermaid shortcode or Mermaid codefence is present
disableMermaid = false
# Specifies the remote location of the Mermaid js
customMermaidURL = "https://unpkg.com/mermaid/dist/mermaid.min.js"
# Initialization parameter for Mermaid, see Mermaid documentation
mermaidInitialize = "{ \"theme\": \"default\" }"
# If set to false, load the Swagger module on every page regardless if a Swagger shortcode is present
disableSwagger = false
# Specifies the remote location of the RapiDoc js
customSwaggerURL = "https://unpkg.com/rapidoc/dist/rapidoc-min.js"
# Initialization parameter for Swagger, see RapiDoc documentation
swaggerInitialize = "{ \"theme\": \"light\" }"
# Hide Next and Previous page buttons normally displayed full height beside content
disableNextPrev = true
# Order sections in menu by "weight" or "title". Default to "weight";
# this can be overridden in the pages frontmatter
ordersectionsby = "weight"
# Change default color scheme with a variant one. Eg. can be "auto", "red", "blue", "green" or an array like [ "blue", "green" ].
themeVariant = "auto"
# Change the title separator. Default to "::".
titleSeparator = "-"
# If set to true, the menu in the sidebar will be displayed in a collapsible tree view. Although the functionality works with old browsers (IE11), the display of the expander icons is limited to modern browsers
collapsibleMenu = true
# If a single page can contain content in multiple languages, add those here
additionalContentLanguage = [ "en" ]
# If set to true, no index.html will be appended to prettyURLs; this will cause pages not
# to be servable from the file system
disableExplicitIndexURLs = false
# For external links you can define how they are opened in your browser; this setting will only be applied to the content area but not the shortcut menu
externalLinkTarget = "_blank"

26
docs/content/_index.md Normal file
View File

@ -0,0 +1,26 @@
+++
archetype = "home"
title = "Backy"
+++
Backy is a tool for automating data backup and remote command execution. It can work over SSH, and provides completion and failure notifications, error reporting, and more.
Why the name Backy? Because I wanted an app for backups.
{{% notice tip %}}
Feel free to open a [PR](https://git.andrewnw.xyz/CyberShell/backy/pulls), raise an [issue](https://git.andrewnw.xyz/CyberShell/backy/issues "Open a Gitea Issue")(s), or request new feature(s).
{{% /notice %}}
## Features
- Allows easy configuration of executable commands
- Allows for commands to be run on many hosts over SSH
- Commands can be grouped in list to run in specific order
- Notifications on completion and failure
- Run in cron mode
- For any command, especially backup commands

101
docs/content/cli/_index.md Normal file
View File

@ -0,0 +1,101 @@
---
title: CLI
weight: 4
---
This page lists documentation for the CLI.
## Backy
```
Backy is a command-line application useful for configuring backups, or any commands run in sequence.
Usage:
backy [command]
Available Commands:
backup Runs commands defined in config file.
completion Generate the autocompletion script for the specified shell
cron Starts a scheduler that runs lists defined in config file.
exec Runs commands defined in config file in order given.
help Help about any command
version Prints the version and exits
Flags:
-f, --config string config file to read from
-h, --help help for backy
-v, --verbose Sets verbose level
Use "backy [command] --help" for more information about a command.
```
# Subcommands
## backup
```
Backup executes commands defined in config file.
Use the --lists or -l flag to execute the specified lists. If not flag is not given, all lists will be executed.
Usage:
backy backup [--lists=list1,list2,... | -l list1, list2,...] [flags]
Flags:
-h, --help help for backup
-l, --lists strings Accepts comma-separated names of command lists to execute.
Global Flags:
-f, --config string config file to read from
-v, --verbose Sets verbose level
```
## cron
```
Cron starts a scheduler that executes command lists at the time defined in config file.
Usage:
backy cron [flags]
Flags:
-h, --help help for cron
Global Flags:
-f, --config string config file to read from
-v, --verbose Sets verbose level
```
## exec
```
Exec executes commands defined in config file in order given.
Usage:
backy exec command ... [flags]
Flags:
-h, --help help for exec
Global Flags:
-f, --config string config file to read from
-v, --verbose Sets verbose level
```
## version
```
Prints the version and exits. No arguments just prints the version number only.
Usage:
backy version [flags]
Flags:
-h, --help help for version
-n, --num Output the version number only. (default true)
-V, --vpre Output the version with v prefixed.
Global Flags:
-f, --config string config file to read from
-v, --verbose Sets verbose level
```

View File

@ -0,0 +1,22 @@
---
title: "Configuring Backy"
weight: 3
description: >
This page tells you how to configure Backy.
---
This is the section on the config file.
To use a specific file:
```backy [command] -f /path/to/file```
If you leave the config path blank, the following paths will be searched in order:
- `./backy.yml`
- `./backy.yaml`
- `~/.config/backy.yml`
- `~/.config/backy.yaml`
Create a file at `~/.config/backy.yml`.
See the rest of the documentation in this section to configure it.

View File

@ -0,0 +1,85 @@
---
title: "Command Lists"
weight: 2
description: >
This page tells you how to get started with Backy.
---
Command lists are for executing commands in sequence and getting notifications from them.
The top-level object key can be anything you want but not the same as another.
```yaml
test2:
name: test2
order:
- test
- test2
notifications:
- prod-email
- matrix
cron: "0 * * * * *"
```
| key | description | type | required
| --- | --- | --- | --- |
| `order` | Defines the sequence of commands to execute | `[]string` | yes |
| `getOutput` | Command(s) output is in the notification(s) | `bool` | no |
| `notifications` | The notification IDs to use on success and failure | `[]string` | no |
| `name` | Optional name of the list | `string` | no |
| `cron` | Time at which to schedule the list. | `string` | no |
### Order
The order is an array of commands to execute in order. Each command must be defined.
```yaml
order:
- cmd-1
- cmd-2
```
### getOutput
Get command output when a notification is sent.
Is not required. Can be `true` or `false`.
### Notifications
An array of notification IDs to use on success and failure. Must match any of the `notifications` object map keys.
### Name
Name is optional for logging. If name is not defined, name will be the object's map key.
### Cron mode
Backy also has a cron mode, so one can run `backy cron` and start a process that schedules jobs to run at times defined in the configuration file.
Adding `cron: 0 0 1 * * *` to a `cmd-configs` object will schedule the list at 1 in the morning. See [https://crontab.guru/](https://crontab.guru/) for reference.
{{% notice tip %}}
Note: Backy uses the second field of cron, so add anything except * to the beginning of a regular cron expression.
{{% /notice %}}
```yaml
cmd-configs:
  cmds-to-run: # this can be any name you want
    # all commands have to be defined
    order:
      - stop-docker-container
      - backup-docker-container-script
      - shell-cmd
      - hostname
    notifications:
      - matrix
    name: backup-some-server
    cron: "0 0 1 * * *"
  hostname:
    name: hostname
    order:
      - hostname
    notifications:
    - prod-email
```

View File

@ -0,0 +1,97 @@
---
title: "Commands"
weight: 1
---
The yaml top-level map can be any string.
The top-level name must be unique.
```yaml
commands:
stop-docker-container:
cmd: docker
Args:
- compose
- -f /some/path/to/docker-compose.yaml
- down
# if host is not defined, command will be run locally
host: some-host
backup-docker-container-script:
cmd: /path/to/script/on/some-host
# The host has to be defined in either the config file or the SSH Config files
host: some-host
environment:
- FOO=BAR
- APP=$VAR
```
Values available for this section:
| name | description | type | required
| --- | --- | --- | --- |
| `cmd` | Defines the command to execute | `string` | yes |
| `args` | Defines the arguments to the command | `[]string` | no |
| `environment` | Defines evironment variables for the command | `[]string` | no |
| `getOutput` | Command(s) output is in the notification(s) | `bool` | no |
| `host` | If not specified, the command will execute locally. | `string` | no |
| `shell` | Only applicable when host is not specified | `string` | no |
#### cmd
cmd must be a valid command or script to execute.
#### args
args must be arguments to cmd as they would be on the command-line:
```sh
cmd [arg1 arg2 ...]
```
Define them in an array:
```yaml
args:
- arg1
- arg2
- arg3
```
### getOutput
Get command output when a notification is sent.
Is not required. Can be `true` or `false`.
#### host
{{% notice info %}}
If any `host` is not defined or left blank, the command will run on the local machine.
{{% /notice %}}
Host may or may not be defined in the `hosts` section.
{{% notice info %}}
If any `host` from the commands section does not match any object in the `hosts` section, the `Host` is assumed to be this value. This value will be used to search in the default SSH config files.
For example, say that I have a host defined in my SSH config with the `Host` defined as `web-prod`.
If I assign a value to host as `host: web-prod` and don't specify this value in the `hosts` object, web-prod will be used as the `Host` in searching the SSH config files.
{{% /notice %}}
### shell
If shell is defined and host is NOT defined, the command will run in the specified shell.
Make sure to escape any shell input.
### environment
The environment variables support expansion:
- using escaped values `$VAR` or `${VAR}`
For now the variables have to be defined in an `.env` file in the same directory as the config file.
If using it with host specified, the SSH server has to be configured to accept those env variables.
If the command is run locally, the OS's environment is added.

View File

@ -0,0 +1,74 @@
---
title: "Notifications"
weight: 3
description: >
This page tells you how to get set up Backy notifications.
---
Notifications can be sent on command list completion and failure.
The supported platforms for notifications are email (SMTP) and [Matrix](https://matrix.org/).
Notifications are defined by type. The top-level object will be the id, and the `type` is required.
```yaml
notifications:
prod-email:
type: mail
host: yourhost.tld
port: 587
senderaddress: email@domain.tld
to:
- admin@domain.tld
username: smtp-username@domain.tld
password: your-password-here
matrix:
type: matrix
home-server: your-home-server.tld
room-id: room-id
access-token: your-access-token
user-id: your-user-id
```
Types recognized are `type: mail` and `type: matrix`
The type's object and its keys are listed below.
### type: mail
| key | description | type
| --- | --- | ---
| `host` | Specifies the SMTP host to connect to | `string`
| `port` | Specifies the SMTP port | `uint16`
| `senderaddress` | Address from which to send mail | `string`
| `to` | Recipients to send emails to | `[]string`
| `username` | SMTP username | `string`
| `password` | SMTP password | `string`
### type: matrix
| key | description | type
| --- | --- | ---
| `home-server` | Specifies the Matrix server connect to | `string`
| `room-id` | Specifies the room ID of the room to send messages to | `string`
| `access-token` | Matrix access token | `string`
| `user-id` | Matrix user ID | `string`
To get your access token (assumes you are using [Element](https://element.io/)) :
1. Log in to the account you want to get the access token for. Click on the name in the top left corner, then "Settings".
2. Click the "Help & About" tab (left side of the dialog).
3. Scroll to the bottom and click on `<click to reveal>` part of Access Token.
4. Copy your access token to a safe place.
To get the room ID:
1. On Element or a similar client, navigate to the room.
2. Navigate to the settings from the top menu.
3. Click on Advanced, the room ID is there.
{{% notice info %}}
Make sure to quote the room ID, as [YAML spec defines tags using `!`](https://yaml.org/spec/1.2.2/#3212-tags).
{{% /notice %}}

View File

@ -0,0 +1,26 @@
---
title: "Vault"
weight: 4
---
[Vault](https://www.vaultproject.io/) is a tool for storing secrets and other data securely.
Vault config can be used by prefixing `vault:` in front of a password or ENV var.
This is the object in the config file:
```yaml
vault:
token: hvs.tXqcASvTP8wg92f7riyvGyuf
address: http://127.0.0.1:8200
enabled: false
keys:
- name: mongourl
mountpath: secret
path: mongo/url
type: # KVv1 or KVv2
- name:
path:
type:
mountpath:
```

View File

@ -0,0 +1,10 @@
---
title: "Getting started"
weight: 2
description: >
This page tells you how to get started with Backy.
---
If you have not installed Backy, [see the install documentation](install).
If you need to configure it, [see the config page](config).

View File

@ -0,0 +1,157 @@
---
title: "Config File Definitions"
description: >
This page tells you how to configure Backy.
---
### Commands
The commands section is for defining commands. These can be run with or without a shell and on a host or locally.
See the [commands documentation](/config/commands) for further information.
```yaml
commands:
  stop-docker-container:
output: true # Optional and only when run in list and notifications are sent
    cmd: docker
    args:
      - compose
      - -f /some/path/to/docker-compose.yaml
      - down
    # if host is not defined, cmd will be run locally
    host: some-host 
  backup-docker-container-script:
    cmd: /path/to/script
    # The host has to be defined in the config file
    host: some-host
    environment:
      - FOO=BAR
    - APP=$VAR # defined in .env file in config directory
  shell-cmd:
    cmd: rsync
    shell: bash
    args:
      - -av
- some-host:/path/to/data
- ~/Docker/Backups/docker-data
script:
type: scriptFile # run a local script on a remote host
cmd: path/to/your/script.sh
host: some-host
  hostname:
    cmd: hostname
```
### Lists
To execute groups of commands in sequence, use a list configuration.
```yaml
cmd-configs:
cmds-to-run: # this can be any name you want
# all commands have to be defined in the commands section
order:
- stop-docker-container
- backup-docker-container-script
- shell-cmd
- hostname
getOutput: true # Optional and only for when notifications are sent
notifications:
- matrix
name: backup-some-server
hostname:
name: hostname
order:
- hostname
notifications:
- prod-email
```
### Hosts
The hosts object may or may not be defined.
{{% notice info %}}
If any `host` from a commands object does not match any `host` object, the needed values will be checked in the default SSH config files.
{{% /notice %}}
```yaml
hosts:
# any needed ssh_config(5) keys/values not listed here will be looked up in the config file or the default config file
some-host:
hostname: some-hostname
config: ~/.ssh/config
user: user
privatekeypath: /path/to/private/key
port: 22
# can also be env:VAR or the password itself
password: file:/path/to/file
# can also be env:VAR or the password itself
privatekeypassword: file:/path/to/file
# only one is supported for now
proxyjump: some-proxy-host
```
### Notifications
The notifications object can have two forms.
For more, [see the notification object documentation](/config/notifications). The top-level map key is id that has to be referenced by the `cmd-configs` key `notifications`.
```yaml
notifications:
prod-email:
type: mail
host: yourhost.tld
port: 587
senderAddress: email@domain.tld
recipients:
- admin@domain.tld
username: smtp-username@domain.tld
password: your-password-here
matrix:
type: matrix
home-server: your-home-server.tld
room-id: room-id
access-token: your-access-token
user-id: your-user-id
```
### Logging
cmd-std-out controls whether commands output is echoed to StdOut.
If logfile is not defined, the log file will be written to the config directory in the file `backy.log`.
`console-disabled` controls whether the logging messages are echoed to StdOut. Default is false.
`verbose` basically does nothing as all necessary info is already output.
```yaml
logging:
verbose: false
file: path/to/log/file.log
console-disabled: false
cmd-std-out: false
```
### Vault
[Vault](https://www.vaultproject.io/) can be used to get some configuration values and ENV variables securely.
```
vault:
token: hvs.tXqcASvTP8wg92f7riyvGyuf
address: http://127.0.0.1:8200
enabled: false
keys:
- name: mongourl
mountpath: secret
path: mongo/url
type: # KVv1 or KVv2
- name:
path:
type:
mountpath:
```

View File

@ -0,0 +1,21 @@
---
title: "Install Backy"
weight: 1
description: >
This page tells you how to install Backy.
---
Binaries are available from the [release page](https://git.andrewnw.xyz/CyberShell/backy/releases). Make sure to get the correct version for your system, which supports x86_64, ARM64, and i386.
### Source Install
You can install from source. You will need [Go installed](https://go.dev/doc/install).
Then run:
```bash
go install git.andrewnw.xyz/CyberShell/backy@master
```
Once set, jump over to the [config docs](/getting-started/config) and start configuring your file.

View File

@ -0,0 +1,10 @@
---
title: "Repositories"
weight: 5
---
The repo mirrors are:
* [https://git.andrewnw.xyz/CyberShell/backy](https://git.andrewnw.xyz/CyberShell/backy)
* [https://git.vern.cc/cybershell/backy](https://git.vern.cc/cybershell/backy)
* [https://github.com/CybersShell/backy](https://github.com/CybersShell/backy)

67
docs/frontmatter.json Normal file
View File

@ -0,0 +1,67 @@
{
"$schema": "https://frontmatter.codes/frontmatter.schema.json",
"frontMatter.taxonomy.contentTypes": [
{
"name": "default",
"pageBundle": false,
"previewPath": null,
"fields": [
{
"title": "Title",
"name": "title",
"type": "string"
},
{
"title": "Description",
"name": "description",
"type": "string"
},
{
"title": "Publishing date",
"name": "date",
"type": "datetime",
"default": "{{now}}",
"isPublishDate": true
},
{
"title": "Content preview",
"name": "preview",
"type": "image"
},
{
"title": "Is in draft",
"name": "draft",
"type": "draft"
},
{
"title": "Tags",
"name": "tags",
"type": "tags"
},
{
"title": "Categories",
"name": "categories",
"type": "categories"
}
]
}
],
"frontMatter.framework.id": "hugo",
"frontMatter.content.publicFolder": "static",
"frontMatter.content.pageFolders": [
{
"title": "content",
"path": "[[workspace]]/content",
"originalPath": "[[workspace]]/content"
},
{
"title": "config-main",
"path": "[[workspace]]/content/config",
"originalPath": "[[workspace]]/content/config"
},
{
"title": "gs",
"path": "[[workspace]]/content/getting-started"
}
]
}

5
docs/go.mod Normal file
View File

@ -0,0 +1,5 @@
module git.andrewnw.xyz/CyberShell/backy-docs
go 1.19
require github.com/McShelby/hugo-theme-relearn v0.0.0-20230209073138-890d12ea922d // indirect

2
docs/go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/McShelby/hugo-theme-relearn v0.0.0-20230209073138-890d12ea922d h1:weq1mrQ/qNAvGrNgvZVL1K8adbT3bswZf2ABLr/LCIA=
github.com/McShelby/hugo-theme-relearn v0.0.0-20230209073138-890d12ea922d/go.mod h1:mKQQdxZNIlLvAj8X3tMq+RzntIJSr9z7XdzuMomt0IM=

File diff suppressed because one or more lines are too long

66
docs/public/404.html Normal file
View File

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="height=device-height, width=device-width, initial-scale=1.0, minimum-scale=1.0">
<meta name="generator" content="Hugo 0.110.0">
<meta name="generator" content="Relearn 5.11.2">
<meta name="description" content="">
<title>404 Page not found - A tool for commands</title>
<!-- https://github.com/filamentgroup/loadCSS/blob/master/README.md#how-to-use -->
<link href="/css/fontawesome-all.min.css?1676866111" rel="stylesheet" media="print" onload="this.media='all';this.onload=null;"><noscript><link href="/css/fontawesome-all.min.css?1676866111" rel="stylesheet"></noscript>
<link href="/css/auto-complete.css?1676866111" rel="stylesheet" media="print" onload="this.media='all';this.onload=null;"><noscript><link href="/css/auto-complete.css?1676866111" rel="stylesheet"></noscript>
<link href="/css/perfect-scrollbar.min.css?1676866111" rel="stylesheet">
<link href="/css/nucleus.css?1676866111" rel="stylesheet">
<link href="/css/fonts.css?1676866111" rel="stylesheet" media="print" onload="this.media='all';this.onload=null;"><noscript><link href="/css/fonts.css?1676866111" rel="stylesheet"></noscript>
<link href="/css/theme.css?1676866111" rel="stylesheet">
<link href="/css/theme-auto.css?1676866111" rel="stylesheet" id="variant-style">
<link href="/css/ie.css?1676866111" rel="stylesheet">
<link href="/css/variant.css?1676866111" rel="stylesheet">
<link href="/css/print.css?1676866111" rel="stylesheet" media="print">
<script src="/js/url.js?1676866111"></script>
<script src="/js/variant.js?1676866111"></script>
<script>
// hack to let hugo tell us how to get to the root when using relativeURLs, it needs to be called *url= for it to do its magic:
// https://github.com/gohugoio/hugo/blob/145b3fcce35fbac25c7033c91c1b7ae6d1179da8/transform/urlreplacers/absurlreplacer.go#L72
window.index_js_url="/index.search.js";
var root_url="/";
var baseUri=root_url.replace(/\/$/, '');
// translations
window.T_Copy_to_clipboard = 'Copy to clipboard';
window.T_Copied_to_clipboard = 'Copied to clipboard!';
window.T_Copy_link_to_clipboard = 'Copy link to clipboard';
window.T_Link_copied_to_clipboard = 'Copied link to clipboard!';
window.T_No_results_found = 'No results found for \u0022{0}\u0022';
window.T_N_results_found = '{1} results found for \u0022{0}\u0022';
// some further base stuff
var baseUriFull='http:\/\/example.org/';
window.variants && variants.init( [ 'auto' ] );
</script>
<style>
p, li, ul {
text-align: center
}
ul {
list-style-type: none;
}
</style>
</head>
<body class="mobile-support" data-url="/404.html">
<div id="body" class="default-animation" style="margin-left:0px;">
<div id="sidebar-overlay"></div>
<main id="body-inner" class="chapter" tabindex="-1">
<div class="flex-block-wrapper">
<article class="default">
<h1 id="error">Error</h1>
<p></p>
<p>Woops. Looks like this page doesn&#39;t exist ¯\_(ツ)_/¯.</p>
<p></p>
<p><a href="/index.html">Go to homepage</a></p>
<p><img src="/images/gopher-404.jpg" style="width:50%" alt="Page not found!"></p>
</article>
</div>
</main>
</div>
</body>
</html>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Categories on A tool for commands</title>
<link>http://example.org/categories/index.html</link>
<description>Recent content in Categories on A tool for commands</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language><atom:link href="http://example.org/categories/index.xml" rel="self" type="application/rss+xml" />
</channel>
</rss>

303
docs/public/cli/index.html Normal file

File diff suppressed because one or more lines are too long

10
docs/public/cli/index.xml Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>CLI on A tool for commands</title>
<link>http://example.org/cli/index.html</link>
<description>Recent content in CLI on A tool for commands</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language><atom:link href="http://example.org/cli/index.xml" rel="self" type="application/rss+xml" />
</channel>
</rss>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Configuring Backy on A tool for commands</title>
<link>http://example.org/config/index.html</link>
<description>Recent content in Configuring Backy on A tool for commands</description>
<generator>Hugo -- gohugo.io</generator>
<language>en-us</language><atom:link href="http://example.org/config/index.xml" rel="self" type="application/rss+xml" />
<item>
<title>Command Lists</title>
<link>http://example.org/config/command-lists/index.html</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://example.org/config/command-lists/index.html</guid>
<description>Command lists are for executing commands in sequence and getting notifications from them.
The top-level object key can be anything you want.
key description type required order Defines the sequence of commands to execute []string yes notifications The notification IDs to use on success and failure []string no name Optional name of the list string no cron Time at which to schedule the list. string no Order The order is an array of commands to execute in order.</description>
</item>
<item>
<title>Commands</title>
<link>http://example.org/config/commands/index.html</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://example.org/config/commands/index.html</guid>
<description>The yaml top-level map can be any string.
The top-level name must be unique.
commands: stop-docker-container: cmd: docker Args: - compose - -f /some/path/to/docker-compose.yaml - down # if host is not defined, command will be run locally host: some-host backup-docker-container-script: cmd: /path/to/script # The host has to be defined in either the config file or the SSH Config files host: some-host environment: - FOO=BAR - APP=$VAR Values available for this section:</description>
</item>
<item>
<title>Notifications</title>
<link>http://example.org/config/notifications/index.html</link>
<pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
<guid>http://example.org/config/notifications/index.html</guid>
<description>Notifications can be sent on command list completion and failure.
The supported platforms for notifications are email (SMTP) and Matrix.
Notifications are defined by type. The top-level object will be the id, and the type is required.
Info Type in a cmd-configs object must match one of these.
notifications: prod-email: type: mail host: yourhost.tld port: 587 senderaddress: email@domain.tld to: - admin@domain.tld username: smtp-username@domain.tld password: your-password-here matrix: type: matrix home-server: your-home-server.tld room-id: room-id access-token: your-access-token user-id: your-user-id Types recognized are type: mail and type: matrix</description>
</item>
</channel>
</rss>

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,58 @@
.autocomplete-suggestions {
text-align: left;
cursor: default;
border: 1px solid #ccc;
border-top: 0;
background: #fff;
box-shadow: -1px 1px 3px rgba(0,0,0,.1);
/* core styles should not be changed */
position: absolute;
display: none;
z-index: 9999;
max-height: 150px;
max-height: calc( 100vh - 150px );
overflow: hidden;
overflow-y: auto;
box-sizing: border-box;
}
.autocomplete-suggestion {
position: relative;
cursor: pointer;
padding: 7px;
line-height: 23px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #282828;
}
.autocomplete-suggestion b {
font-weight: normal;
color: #1f8dd6;
}
.autocomplete-suggestion.selected {
background: #282828;
color: #fff;
}
.autocomplete-suggestion:hover,
.autocomplete-suggestion:focus,
.autocomplete-suggestion:active,
.autocomplete-suggestion:hover > .context,
.autocomplete-suggestion:focus > .context,
.autocomplete-suggestion:active > .context,
#searchresults .autocomplete-suggestion:hover > .context,
#searchresults .autocomplete-suggestion:focus > .context,
#searchresults .autocomplete-suggestion:active > .context {
background: #383838;
color: #fff;
}
.autocomplete-suggestion > .context {
font-size: 12px;
margin-inline-start: 1em;
overflow: hidden;
text-overflow: ellipsis;
}

View File

@ -0,0 +1,83 @@
/* based on base16-snazzy
/* Background */ .chroma { color: #e2e4e5; background-color: #282a36 }
/* Other */ .chroma .x { }
/* Error */ .chroma .err { color: #ff5c57 }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* Keyword */ .chroma .k { color: #ff6ac1 }
/* KeywordConstant */ .chroma .kc { color: #ff6ac1 }
/* KeywordDeclaration */ .chroma .kd { color: #ff5c57 }
/* KeywordNamespace */ .chroma .kn { color: #ff6ac1 }
/* KeywordPseudo */ .chroma .kp { color: #ff6ac1 }
/* KeywordReserved */ .chroma .kr { color: #ff6ac1 }
/* KeywordType */ .chroma .kt { color: #9aedfe }
/* Name */ .chroma .n { }
/* NameAttribute */ .chroma .na { color: #57c7ff }
/* NameBuiltin */ .chroma .nb { color: #ff5c57 }
/* NameBuiltinPseudo */ .chroma .bp { }
/* NameClass */ .chroma .nc { color: #f3f99d }
/* NameConstant */ .chroma .no { color: #ff9f43 }
/* NameDecorator */ .chroma .nd { color: #ff9f43 }
/* NameEntity */ .chroma .ni { }
/* NameException */ .chroma .ne { }
/* NameFunction */ .chroma .nf { color: #57c7ff }
/* NameFunctionMagic */ .chroma .fm { }
/* NameLabel */ .chroma .nl { color: #ff5c57 }
/* NameNamespace */ .chroma .nn { }
/* NameOther */ .chroma .nx { }
/* NameProperty */ .chroma .py { }
/* NameTag */ .chroma .nt { color: #ff6ac1 }
/* NameVariable */ .chroma .nv { color: #ff5c57 }
/* NameVariableClass */ .chroma .vc { color: #ff5c57 }
/* NameVariableGlobal */ .chroma .vg { color: #ff5c57 }
/* NameVariableInstance */ .chroma .vi { color: #ff5c57 }
/* NameVariableMagic */ .chroma .vm { }
/* Literal */ .chroma .l { }
/* LiteralDate */ .chroma .ld { }
/* LiteralString */ .chroma .s { color: #5af78e }
/* LiteralStringAffix */ .chroma .sa { color: #5af78e }
/* LiteralStringBacktick */ .chroma .sb { color: #5af78e }
/* LiteralStringChar */ .chroma .sc { color: #5af78e }
/* LiteralStringDelimiter */ .chroma .dl { color: #5af78e }
/* LiteralStringDoc */ .chroma .sd { color: #5af78e }
/* LiteralStringDouble */ .chroma .s2 { color: #5af78e }
/* LiteralStringEscape */ .chroma .se { color: #5af78e }
/* LiteralStringHeredoc */ .chroma .sh { color: #5af78e }
/* LiteralStringInterpol */ .chroma .si { color: #5af78e }
/* LiteralStringOther */ .chroma .sx { color: #5af78e }
/* LiteralStringRegex */ .chroma .sr { color: #5af78e }
/* LiteralStringSingle */ .chroma .s1 { color: #5af78e }
/* LiteralStringSymbol */ .chroma .ss { color: #5af78e }
/* LiteralNumber */ .chroma .m { color: #ff9f43 }
/* LiteralNumberBin */ .chroma .mb { color: #ff9f43 }
/* LiteralNumberFloat */ .chroma .mf { color: #ff9f43 }
/* LiteralNumberHex */ .chroma .mh { color: #ff9f43 }
/* LiteralNumberInteger */ .chroma .mi { color: #ff9f43 }
/* LiteralNumberIntegerLong */ .chroma .il { color: #ff9f43 }
/* LiteralNumberOct */ .chroma .mo { color: #ff9f43 }
/* Operator */ .chroma .o { color: #ff6ac1 }
/* OperatorWord */ .chroma .ow { color: #ff6ac1 }
/* Punctuation */ .chroma .p { }
/* Comment */ .chroma .c { color: #78787e }
/* CommentHashbang */ .chroma .ch { color: #78787e }
/* CommentMultiline */ .chroma .cm { color: #78787e }
/* CommentSingle */ .chroma .c1 { color: #78787e }
/* CommentSpecial */ .chroma .cs { color: #78787e }
/* CommentPreproc */ .chroma .cp { color: #78787e }
/* CommentPreprocFile */ .chroma .cpf { color: #78787e }
/* Generic */ .chroma .g { }
/* GenericDeleted */ .chroma .gd { color: #ff5c57 }
/* GenericEmph */ .chroma .ge { text-decoration: underline }
/* GenericError */ .chroma .gr { color: #ff5c57 }
/* GenericHeading */ .chroma .gh { font-weight: bold }
/* GenericInserted */ .chroma .gi { font-weight: bold }
/* GenericOutput */ .chroma .go { color: #43454f }
/* GenericPrompt */ .chroma .gp { }
/* GenericStrong */ .chroma .gs { font-style: italic }
/* GenericSubheading */ .chroma .gu { font-weight: bold }
/* GenericTraceback */ .chroma .gt { }
/* GenericUnderline */ .chroma .gl { text-decoration: underline }
/* TextWhitespace */ .chroma .w { }

View File

@ -0,0 +1,89 @@
/* this variant does not work well if we use fallback styles for IE11 so better
ignore this variant in IE completely */
@supports not (-ms-high-contrast:none) {
/* based on rrt
/* Background */ .chroma { color: #f8f8f2; background-color: #000000 }
/* Other */ .chroma .x { }
/* Error */ .chroma .err { }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7c7c79 }
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7c7c79 }
/* Keyword */ .chroma .k { color: #ff0000 }
/* KeywordConstant */ .chroma .kc { color: #ff0000 }
/* KeywordDeclaration */ .chroma .kd { color: #ff0000 }
/* KeywordNamespace */ .chroma .kn { color: #ff0000 }
/* KeywordPseudo */ .chroma .kp { color: #ff0000 }
/* KeywordReserved */ .chroma .kr { color: #ff0000 }
/* KeywordType */ .chroma .kt { color: #ee82ee }
/* Name */ .chroma .n { }
/* NameAttribute */ .chroma .na { }
/* NameBuiltin */ .chroma .nb { }
/* NameBuiltinPseudo */ .chroma .bp { }
/* NameClass */ .chroma .nc { }
/* NameConstant */ .chroma .no { color: #7fffd4 }
/* NameDecorator */ .chroma .nd { }
/* NameEntity */ .chroma .ni { }
/* NameException */ .chroma .ne { }
/* NameFunction */ .chroma .nf { color: #ffff00 }
/* NameFunctionMagic */ .chroma .fm { }
/* NameLabel */ .chroma .nl { }
/* NameNamespace */ .chroma .nn { }
/* NameOther */ .chroma .nx { }
/* NameProperty */ .chroma .py { }
/* NameTag */ .chroma .nt { }
/* NameVariable */ .chroma .nv { color: #eedd82 }
/* NameVariableClass */ .chroma .vc { }
/* NameVariableGlobal */ .chroma .vg { }
/* NameVariableInstance */ .chroma .vi { }
/* NameVariableMagic */ .chroma .vm { }
/* Literal */ .chroma .l { }
/* LiteralDate */ .chroma .ld { }
/* LiteralString */ .chroma .s { color: #87ceeb }
/* LiteralStringAffix */ .chroma .sa { color: #87ceeb }
/* LiteralStringBacktick */ .chroma .sb { color: #87ceeb }
/* LiteralStringChar */ .chroma .sc { color: #87ceeb }
/* LiteralStringDelimiter */ .chroma .dl { color: #87ceeb }
/* LiteralStringDoc */ .chroma .sd { color: #87ceeb }
/* LiteralStringDouble */ .chroma .s2 { color: #87ceeb }
/* LiteralStringEscape */ .chroma .se { color: #87ceeb }
/* LiteralStringHeredoc */ .chroma .sh { color: #87ceeb }
/* LiteralStringInterpol */ .chroma .si { color: #87ceeb }
/* LiteralStringOther */ .chroma .sx { color: #87ceeb }
/* LiteralStringRegex */ .chroma .sr { color: #87ceeb }
/* LiteralStringSingle */ .chroma .s1 { color: #87ceeb }
/* LiteralStringSymbol */ .chroma .ss { color: #ff6600 }
/* LiteralNumber */ .chroma .m { color: #ff6600 }
/* LiteralNumberBin */ .chroma .mb { color: #ff6600 }
/* LiteralNumberFloat */ .chroma .mf { color: #ff6600 }
/* LiteralNumberHex */ .chroma .mh { color: #ff6600 }
/* LiteralNumberInteger */ .chroma .mi { color: #ff6600 }
/* LiteralNumberIntegerLong */ .chroma .il { color: #ff6600 }
/* LiteralNumberOct */ .chroma .mo { color: #ff6600 }
/* Operator */ .chroma .o { }
/* OperatorWord */ .chroma .ow { }
/* Punctuation */ .chroma .p { }
/* Comment */ .chroma .c { color: #00ff00 }
/* CommentHashbang */ .chroma .ch { color: #00ff00 }
/* CommentMultiline */ .chroma .cm { color: #00ff00 }
/* CommentSingle */ .chroma .c1 { color: #00ff00 }
/* CommentSpecial */ .chroma .cs { color: #00ff00 }
/* CommentPreproc */ .chroma .cp { color: #e5e5e5 }
/* CommentPreprocFile */ .chroma .cpf { color: #e5e5e5 }
/* Generic */ .chroma .g { }
/* GenericDeleted */ .chroma .gd { }
/* GenericEmph */ .chroma .ge { }
/* GenericError */ .chroma .gr { }
/* GenericHeading */ .chroma .gh { }
/* GenericInserted */ .chroma .gi { }
/* GenericOutput */ .chroma .go { }
/* GenericPrompt */ .chroma .gp { }
/* GenericStrong */ .chroma .gs { }
/* GenericSubheading */ .chroma .gu { }
/* GenericTraceback */ .chroma .gt { }
/* GenericUnderline */ .chroma .gl { }
/* TextWhitespace */ .chroma .w { }
}

View File

@ -0,0 +1,83 @@
/* based on monokai
/* Background */ .chroma { color: #f8f8f8; background-color: #2b2b2b }
/* Other */ .chroma .x { }
/* Error */ .chroma .err { color: #960050; }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
/* Keyword */ .chroma .k { color: #66d9ef }
/* KeywordConstant */ .chroma .kc { color: #66d9ef }
/* KeywordDeclaration */ .chroma .kd { color: #66d9ef }
/* KeywordNamespace */ .chroma .kn { color: #f92672 }
/* KeywordPseudo */ .chroma .kp { color: #66d9ef }
/* KeywordReserved */ .chroma .kr { color: #66d9ef }
/* KeywordType */ .chroma .kt { color: #66d9ef }
/* Name */ .chroma .n { }
/* NameAttribute */ .chroma .na { color: #a6e22e }
/* NameBuiltin */ .chroma .nb { }
/* NameBuiltinPseudo */ .chroma .bp { }
/* NameClass */ .chroma .nc { color: #a6e22e }
/* NameConstant */ .chroma .no { color: #66d9ef }