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      }