github.com/safing/portbase@v0.19.5/README.md (about) 1 > **Check out our main project at [safing/portmaster](https://github.com/safing/portmaster)** 2 3 # Portbase 4 5 Portbase helps you quickly take off with your project. It gives you all the basic needs you would have for a service (_not_ tool!). 6 Here is what is included: 7 8 - `log`: really fast and beautiful logging 9 - `modules`: a multi stage, dependency aware boot process for your software, also manages tasks 10 - `config`: simple, live updating and extremely fast configuration storage 11 - `info`: easily tag your builds with versions, commit hashes, and so on 12 - `formats`: some handy data encoding libs 13 - `rng`: a feedable CSPRNG for great randomness 14 - `database`: intelligent and syncable database with hooks and easy integration with structs, uses buckets with different backends 15 - `api`: a websocket interface to the database, can be extended with custom http handlers 16 17 Before you continue, a word about this project. It was created to hold the base code for both Portmaster and Gate17. This is also what it will be developed for. If you have a great idea on how to improve portbase, please, by all means, raise an issue and tell us about it, but please also don't be surprised or offended if we ask you to create your own fork to do what you need. Portbase isn't for everyone, it's quite specific to our needs, but we decided to make it easily available to others. 18 19 Portbase is actively maintained, please raise issues. 20 21 ## log 22 23 The main goal of this logging package is to be as fast as possible. Logs are sent to a channel only with minimal processing beforehand, so that the service can continue with the important work and write the logs later. 24 25 Second, is beauty, both in form what information is provided and how. 26 27 You can use flags to change the log level on a source file basis. 28 29 ## modules <small>requires `log`</small> 30 31 packages may register themselves as modules, to take part in the multi stage boot and coordinated shutdown. 32 33 Registering only requires a name/key and the `prep()`, `start()` and `stop()` functions. 34 35 This is how modules are booted: 36 37 - `init()` available: ~~flags~~, ~~config~~, ~~logging~~, ~~dependencies~~ 38 - register flags (with the stdlib `flag` library) 39 - register module 40 - `module.prep()` available: flags, ~~config~~, ~~logging~~, ~~dependencies~~ 41 - react to flags 42 - register config variables 43 - if an error occurs, return it 44 - return ErrCleanExit for a clean, successful exit. (eg. you only printed a version) 45 - `module.start()` available: flags, config, logging, dependencies 46 - start tasks and workers 47 - do not log errors while starting, but return them 48 - `module.stop()` available: flags, config, logging, dependencies 49 - stop all work (ie. goroutines) 50 - do not log errors while stopping, but return them 51 52 You can start tasks and workers from your module that are then integrated into the module system and will allow for insights and better control of them in the future. 53 54 ## config <small>requires `log`</small> 55 56 The config package stores the configuration in json strings. This may sound a bit weird, but it's very practical. 57 58 There are three layers of configuration - in order of priority: user configuration, default configuration and the fallback values supplied when registering a config variable. 59 60 When using config variables, you get a function that checks if your config variable is still up to date every time. If it did not change, it's _extremely_ fast. But if it, it will fetch the current value, which takes a short while, but does not happen often. 61 62 // This is how you would get a string config variable function. 63 myVar := GetAsString("my_config_var", "default") 64 // You then use myVar() directly every time, except when you must guarantee the same value between two calls 65 if myVar() != "default" { 66 log.Infof("my_config_var is set to %s", myVar()) 67 } 68 // no error handling needed! :) 69 70 WARNING: While these config variable functions are _extremely_ fast, they are _NOT_ thread/goroutine safe! (Use the `Concurrent` wrapper for that!) 71 72 ## info 73 74 Info provides a easy way to store your version and build information within the binary. If you use the `build` script to build the program, it will automatically set build information so that you can easily find out when and from which commit a binary was built. 75 76 The `build` script extracts information from the host and the git repo and then calls `go build` with some additional arguments. 77 78 ## formats/varint 79 80 This is just a convenience wrapper around `encoding/binary`, because we use varints a lot. 81 82 ## formats/dsd <small>requires `formats/varint`</small> 83 84 DSD stands for dynamically structured data. In short, this a generic packer that reacts to the supplied data type. 85 86 - structs are usually json encoded 87 - []bytes and strings stay the same 88 89 This makes it easier / more efficient to store different data types in a k/v data storage. 90 91 ## rng <small>requires `log`, `config`</small> 92 93 This package provides a CSPRNG based on the [Fortuna](https://en.wikipedia.org/wiki/Fortuna_(PRNG)) CSPRNG, devised by Bruce Schneier and Niels Ferguson. Implemented by Jochen Voss, published [on Github](https://github.com/seehuhn/fortuna). 94 95 Only the Generator is used from the `fortuna` package. The feeding system implemented here is configurable and is focused with efficiency in mind. 96 97 While you can feed the RNG yourself, it has two feeders by default: 98 - It starts with a seed from `crypto/rand` and periodically reseeds from there 99 - A really simple tickfeeder which extracts entropy from the internal go scheduler using goroutines and is meant to be used under load. 100 101 ## database <small>requires `log`</small> 102 _introduction to be written_ 103 104 ## api <small>requires `log`, `database`, `config`</small> 105 _introduction to be written_ 106 107 ## The main program 108 109 If you build everything with modules, your main program should be similar to this - just use an empty import for the modules you need: 110 111 import ( 112 "os" 113 "os/signal" 114 "syscall" 115 116 "github.com/safing/portbase/info" 117 "github.com/safing/portbase/log" 118 "github.com/safing/portbase/modules" 119 120 // include packages here 121 _ "path/to/my/custom/module" 122 ) 123 124 func main() { 125 126 // Set Info 127 info.Set("MySoftware", "1.0.0") 128 129 // Start 130 err := modules.Start() 131 if err != nil { 132 if err == modules.ErrCleanExit { 133 os.Exit(0) 134 } else { 135 os.Exit(1) 136 } 137 } 138 139 // Shutdown 140 // catch interrupt for clean shutdown 141 signalCh := make(chan os.Signal) 142 signal.Notify( 143 signalCh, 144 os.Interrupt, 145 syscall.SIGHUP, 146 syscall.SIGINT, 147 syscall.SIGTERM, 148 syscall.SIGQUIT, 149 ) 150 select { 151 case <-signalCh: 152 log.Warning("main: program was interrupted") 153 modules.Shutdown() 154 case <-modules.ShuttingDown(): 155 } 156 157 }