Compare commits

..

13 Commits

Author SHA1 Message Date
Keagan McClelland
4713bdc793 Integration/0.2.17 (#789)
* self-repair and prevention of ssl cert renewal bug

* bump to 0.2.17

* adjust ui for 0.2.17

* adds db migration

* add extra protection around ssl directory target

* liftIO
2021-11-11 12:23:25 -07:00
Keagan McClelland
183f91859a fixes apt-update issue 100 where Suites changed, kills update sound t… (#422)
* fixes apt-update issue 100 where Suites changed, kills update sound thread, sets updating to false if synchronization fails

* cleans up code

* appmgr changes

* ui changes

* updates required appmgr in agent

* actually upgrade current version in appmgr
2021-08-23 14:30:08 -06:00
Keagan McClelland
18a069e6fd Release/0.2.15 (#411)
* remove all apt-get calls from check phase of startup

* Fix "n is undefined" config spec change bug (#392)

Fixes the issue where an old config with a new config spec sometimes renders the config inaccessible, with the error `n is undefined`

* updates version numbers

* version change for appmgr

* updates version numbers and welcome message

* adds migration for 0.2.15 to the agent

* actually add 0.2.15 migration to appmgr

Co-authored-by: Chris Guida <chrisguida@users.noreply.github.com>
2021-08-18 15:28:32 -06:00
Keagan McClelland
46643cb3a4 fixes post process build failure step (#381) 2021-07-27 08:00:09 -06:00
Keagan McClelland
70397eaf10 fix agent code review 2021-07-20 16:54:50 -06:00
Keagan McClelland
5caf6b3d90 fix build issues 2021-07-20 16:54:50 -06:00
Keagan McClelland
5e3e330bb3 change release notes 2021-07-20 16:54:50 -06:00
Keagan McClelland
7989f12511 alter semantics of tor update 2021-07-20 16:54:50 -06:00
Keagan McClelland
0ac0da0ebf preps 0.2.14 messaging and version bumps 2021-07-20 16:54:50 -06:00
Keagan McClelland
6334d79c01 updates appmgr to 0.2.14 ceremonial 2021-07-20 16:54:50 -06:00
Keagan McClelland
943c898a3e update appmgr dependency 2021-07-20 16:54:50 -06:00
Keagan McClelland
39f85c7199 agent 0.2.14 2021-07-20 16:54:50 -06:00
kn0wmad
57e9a97d44 Build guide edit 2021-06-29 16:03:13 -06:00
24 changed files with 18379 additions and 1630 deletions

View File

@@ -98,6 +98,10 @@
```
rm -rf ~/.stack/setup-exe-src/
```
1. Re-make the agent
```
make agent
```
6. Install requirements for step 7
1. Install NVM

View File

@@ -5,7 +5,7 @@ cabal-version: 1.12
-- see: https://github.com/sol/hpack
name: ambassador-agent
version: 0.2.13
version: 0.2.17
build-type: Simple
extra-source-files:
./migrations/0.1.0::0.1.0
@@ -19,6 +19,10 @@ extra-source-files:
./migrations/0.2.10::0.2.11
./migrations/0.2.11::0.2.12
./migrations/0.2.12::0.2.13
./migrations/0.2.13::0.2.14
./migrations/0.2.14::0.2.15
./migrations/0.2.15::0.2.16
./migrations/0.2.16::0.2.17
./migrations/0.2.1::0.2.2
./migrations/0.2.2::0.2.3
./migrations/0.2.3::0.2.4

View File

@@ -33,5 +33,5 @@ database:
database: "start9_agent.sqlite3"
poolsize: "_env:YESOD_SQLITE_POOLSIZE:10"
app-mgr-version-spec: "=0.2.13"
app-mgr-version-spec: "=0.2.16"
#analytics: UA-YOURCODE

View File

@@ -0,0 +1 @@
SELECT TRUE;

View File

@@ -0,0 +1 @@
SELECT TRUE;

View File

@@ -0,0 +1 @@
SELECT TRUE;

View File

@@ -0,0 +1 @@
SELECT TRUE;

View File

@@ -1,5 +1,5 @@
name: ambassador-agent
version: 0.2.13
version: 0.2.17
default-extensions:
- NoImplicitPrelude
@@ -182,4 +182,4 @@ executables:
condition: flag(library-only)
- condition: false
other-modules: Paths_ambassador_agent
extra-source-files: ./migrations/*
extra-source-files: ./migrations/*

View File

@@ -6,22 +6,30 @@ import Startlude hiding ( err )
import Data.String.Interpolate ( i )
import System.Process ( system )
import Foundation
import Lib.SystemPaths
import Settings
import Lib.Ssl
import Daemon.ZeroConf ( getStart9AgentHostname )
import Lib.Tor
import Constants
import Control.Carrier.Lift
import System.Directory ( doesPathExist
import Daemon.ZeroConf ( getStart9AgentHostname )
import qualified Data.ByteString as BS
import Database.Persist.Sql ( Filter
, SqlPersistT
, count
, runSqlPool
)
import Foundation
import qualified Lib.Notifications as Notifications
import Lib.Ssl
import Lib.SystemCtl
import Lib.SystemPaths
import Lib.Tor
import Lib.Types.Core
import Model
import Settings
import System.Directory ( createDirectoryIfMissing
, doesPathExist
, removePathForcibly
, renameDirectory
)
import Lib.SystemCtl
import qualified Lib.Notifications as Notifications
import Database.Persist.Sql ( runSqlPool )
import Lib.Types.Core
import Constants
import System.FilePath ( takeDirectory )
renewSslLeafCert :: AgentCtx -> IO ()
renewSslLeafCert ctx = do
@@ -30,7 +38,7 @@ renewSslLeafCert ctx = do
let hostname = sid <> ".local"
tor <- injectFilesystemBase base getAgentHiddenServiceUrl
putStr @Text "SSL Renewal Required? "
needsRenew <- doesSslNeedRenew (toS $ entityCertPath sid `relativeTo` base)
needsRenew <- flip runSqlPool (appConnPool ctx) $ doesSslNeedRenew (toS $ entityCertPath sid `relativeTo` base)
print needsRenew
when needsRenew $ runM . injectFilesystemBase base $ do
intCaKeyPath <- toS <$> getAbsoluteLocationFor intermediateCaKeyPath
@@ -42,6 +50,9 @@ renewSslLeafCert ctx = do
entConfPathTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> entityConfPath sid)
entCertPathTmp <- toS <$> getAbsoluteLocationFor (agentTmpDirectory <> entityCertPath sid)
liftIO $ createDirectoryIfMissing True sslDirTmp
liftIO $ BS.writeFile entConfPathTmp (domain_CSR_CONF hostname)
(ec, out, err) <- writeLeafCert
DeriveCertificate { applicantConfPath = entConfPathTmp
, applicantKeyPath = entKeyPathTmp
@@ -60,24 +71,28 @@ renewSslLeafCert ctx = do
putStrLn @String $ "stdout: " <> out
putStrLn @String $ "stderr: " <> err
case ec of
ExitSuccess -> pure ()
ExitFailure n ->
liftIO
. void
$ flip runSqlPool (appConnPool ctx)
$ Notifications.emit (AppId "EmbassyOS") agentVersion
$ Notifications.CertRenewFailed (ExitFailure n) out err
let sslDir = toS $ sslDirectory `relativeTo` base
liftIO $ removePathForcibly sslDir
liftIO $ renameDirectory sslDirTmp sslDir
liftIO $ systemCtl RestartService "nginx" $> ()
ExitSuccess -> liftIO $ do
let sslDir = toS $ sslDirectory `relativeTo` base
createDirectoryIfMissing True (takeDirectory sslDir)
removePathForcibly sslDir
renameDirectory sslDirTmp sslDir
systemCtl RestartService "nginx" $> ()
doesSslNeedRenew :: FilePath -> IO Bool
doesSslNeedRenew :: FilePath -> SqlPersistT IO Bool
doesSslNeedRenew cert = do
exists <- doesPathExist cert
exists <- liftIO $ doesPathExist cert
if exists
then do
ec <- liftIO $ system [i|openssl x509 -checkend 2592000 -noout -in #{cert}|]
pure $ ec /= ExitSuccess
else pure False
else do
-- if we have set up the embassy already, then this is bad state that needs to be repaired
n <- count ([] :: [Filter Account])
pure $ n >= 1

View File

@@ -5,7 +5,9 @@
{-# LANGUAGE TupleSections #-}
module Lib.SelfUpdate where
import Startlude hiding ( runReader )
import Startlude hiding ( handle
, runReader
)
import Control.Carrier.Error.Either
import Control.Lens
@@ -29,6 +31,7 @@ import Lib.SystemPaths
import Lib.Types.Emver
import Lib.WebServer
import Settings
import UnliftIO.Exception ( handle )
youngAgentPort :: Word16
youngAgentPort = 5960
@@ -191,18 +194,21 @@ runSyncOps syncOps = do
pure res
synchronizeSystemState :: AgentCtx -> Version -> IO ()
synchronizeSystemState ctx _version = handle @SomeException cleanup $ flip runReaderT ctx $ do
synchronizeSystemState ctx _version = handle @_ @SomeException cleanup $ flip runReaderT ctx $ do
(restartsAndRuns, mTid) <- case synchronizer of
Synchronizer { synchronizerOperations } -> flip runStateT Nothing $ for synchronizerOperations $ \syncOp -> do
shouldRun <- lift $ syncOpShouldRun syncOp
putStrLn @Text [i|Sync Op "#{syncOpName syncOp}" should run: #{shouldRun}|]
when shouldRun $ do
whenM (isNothing <$> get) $ do
tid <- liftIO . forkIO . forever $ playSong 300 updateInProgress *> threadDelay 20_000_000
put (Just tid)
tid <- get >>= \case
Nothing -> do
tid <- liftIO . forkIO . forever $ playSong 300 updateInProgress *> threadDelay 20_000_000
put (Just tid)
pure tid
Just tid -> pure tid
putStrLn @Text [i|Running Sync Op: #{syncOpName syncOp}|]
setUpdate True
lift $ syncOpRun syncOp
lift $ handle @_ @SomeException (\e -> lift $ killThread tid *> cleanup e) $ syncOpRun syncOp
pure $ (syncOpRequiresReboot syncOp, shouldRun)
case mTid of
Nothing -> pure ()
@@ -222,5 +228,6 @@ synchronizeSystemState ctx _version = handle @SomeException cleanup $ flip runRe
void $ try @SomeException Sound.stop
void $ try @SomeException Sound.unexport
let e' = InternalE $ show e
setUpdate False
flip runReaderT ctx $ cantFail $ failUpdate e'

View File

@@ -10,8 +10,7 @@ module Lib.Ssl
, root_CA_OPENSSL_CONF
, intermediate_CA_OPENSSL_CONF
, segment
)
where
) where
import Startlude

View File

@@ -24,6 +24,7 @@ import qualified Data.Conduit.Combinators as Conduit
import Data.Conduit.Shell hiding ( arch
, hostname
, patch
, split
, stream
)
import qualified Data.Conduit.Tar as Conduit
@@ -50,6 +51,9 @@ import Constants
import Control.Effect.Error hiding ( run )
import Control.Effect.Labelled ( runLabelled )
import Daemon.ZeroConf ( getStart9AgentHostname )
import Data.ByteString.Char8 ( split )
import qualified Data.ByteString.Char8 as C8
import Data.Conduit.List ( consume )
import qualified Data.Text as T
import Foundation
import Handler.Network
@@ -98,12 +102,12 @@ parseKernelVersion = do
pure $ KernelVersion (Version (major', minor', patch', 0)) arch
synchronizer :: Synchronizer
synchronizer = sync_0_2_13
synchronizer = sync_0_2_17
{-# INLINE synchronizer #-}
sync_0_2_13 :: Synchronizer
sync_0_2_13 = Synchronizer
"0.2.13"
sync_0_2_17 :: Synchronizer
sync_0_2_17 = Synchronizer
"0.2.17"
[ syncCreateAgentTmp
, syncCreateSshDir
, syncRemoveAvahiSystemdDependency
@@ -176,7 +180,7 @@ syncFullUpgrade = SyncOp "Full Upgrade" check migrate True
Just (Done _ (KernelVersion (Version av) _)) -> if av < (4, 19, 118, 0) then pure True else pure False
_ -> pure False
migrate = liftIO . run $ do
shell "apt-get update"
shell "apt-get update --allow-releaseinfo-change"
shell "apt-get full-upgrade -y"
sync32BitKernel :: SyncOp
@@ -201,7 +205,7 @@ syncInstallNginx = SyncOp "Install Nginx" check migrate False
where
check = liftIO . run $ fmap isNothing (shell [i|which nginx || true|] $| conduit await)
migrate = liftIO . run $ do
shell "apt-get update"
shell "apt-get update --allow-releaseinfo-change"
shell "apt-get install nginx -y"
syncInstallEject :: SyncOp
@@ -209,7 +213,7 @@ syncInstallEject = SyncOp "Install Eject" check migrate False
where
check = liftIO . run $ fmap isNothing (shell [i|which eject || true|] $| conduit await)
migrate = liftIO . run $ do
shell "apt-get update"
shell "apt-get update --allow-releaseinfo-change"
shell "apt-get install eject -y"
syncInstallDuplicity :: SyncOp
@@ -217,7 +221,7 @@ syncInstallDuplicity = SyncOp "Install duplicity" check migrate False
where
check = liftIO . run $ fmap isNothing (shell [i|which duplicity || true|] $| conduit await)
migrate = liftIO . run $ do
shell "apt-get update"
shell "apt-get update --allow-releaseinfo-change"
shell "apt-get install -y duplicity"
syncInstallExfatFuse :: SyncOp
@@ -230,7 +234,7 @@ syncInstallExfatFuse = SyncOp "Install exfat-fuse" check migrate False
ProcessException _ (ExitFailure 1) -> pure True
_ -> throwIO e
migrate = liftIO . run $ do
shell "apt-get update"
shell "apt-get update --allow-releaseinfo-change"
shell "apt-get install -y exfat-fuse"
syncInstallExfatUtils :: SyncOp
@@ -243,7 +247,7 @@ syncInstallExfatUtils = SyncOp "Install exfat-utils" check migrate False
ProcessException _ (ExitFailure 1) -> pure True
_ -> throwIO e
migrate = liftIO . run $ do
shell "apt-get update"
shell "apt-get update --allow-releaseinfo-change"
shell "apt-get install -y exfat-utils"
syncInstallLibAvahi :: SyncOp
@@ -256,7 +260,7 @@ syncInstallLibAvahi = SyncOp "Install libavahi-client" check migrate False
ProcessException _ (ExitFailure 1) -> pure True
_ -> throwIO e
migrate = liftIO . run $ do
shell "apt-get update"
shell "apt-get update --allow-releaseinfo-change"
shell "apt-get install -y libavahi-client3"
syncWriteConf :: Text -> ByteString -> SystemPath -> SyncOp
@@ -585,19 +589,32 @@ syncRestarterService = SyncOp "Install Restarter Service" check migrate True
liftIO $ callCommand "systemctl enable restarter.timer"
syncUpgradeTor :: SyncOp
syncUpgradeTor = SyncOp "Install Tor 0.3.5.14-1" check migrate False
syncUpgradeTor = SyncOp "Install Latest Tor" check migrate False
where
check =
liftIO
$ ( run (shell [i|dpkg -l|] $| shell [i|grep tor|] $| shell [i|grep 0.3.5.14-1|] $| conduit await)
$> False
)
`catch` \(e :: ProcessException) -> case e of
ProcessException _ (ExitFailure 1) -> pure True
_ -> throwIO e
check = run $ do
mTorVersion <- (shell "dpkg -s tor" $| shell "grep '^Version'" $| shell "cut -d ' ' -f2" $| conduit await)
let torVersion = case mTorVersion of
Nothing -> panic "invalid output from dpkg, can't read tor version"
Just x -> x
pure $ compareTorVersions torVersion "0.3.5.15-1" == LT
migrate = liftIO . run $ do
shell "apt-get update"
shell "apt-get install -y tor=0.3.5.14-1"
shell "apt-get update --allow-releaseinfo-change"
availVersions <-
(shell "apt-cache madison tor" $| shell "cut -d '|' -f2" $| shell "xargs" $| conduit consume)
latest <- case lastMay $ sortBy compareTorVersions availVersions of
Nothing -> throwIO $ ErrorCall "No available versions of tor"
Just x -> pure x
shell $ "apt-get install -y tor=" <> if "0.3.5.15-1" `elem` availVersions
then "0.3.5.15-1"
else (C8.unpack latest)
compareTorVersions :: ByteString -> ByteString -> Ordering
compareTorVersions a b =
let a' = (traverse (readMaybe @Int . decodeUtf8) . (split '.' <=< split '-') $ a)
b' = (traverse (readMaybe @Int . decodeUtf8) . (split '.' <=< split '-') $ b)
in case liftA2 compare a' b' of
Nothing -> panic "invalid tor version string"
Just x -> x
syncDropCertificateUniqueness :: SyncOp
syncDropCertificateUniqueness = SyncOp "Eliminate OpenSSL unique_subject=yes" check migrate False

4
appmgr/Cargo.lock generated
View File

@@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.14.1"
@@ -41,7 +43,7 @@ checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1"
[[package]]
name = "appmgr"
version = "0.2.13"
version = "0.2.16"
dependencies = [
"async-trait",
"avahi-sys",

View File

@@ -2,7 +2,7 @@
authors = ["Aiden McClelland <me@drbonez.dev>"]
edition = "2018"
name = "appmgr"
version = "0.2.13"
version = "0.2.16"
[lib]
name = "appmgrlib"
@@ -20,7 +20,9 @@ production = []
[dependencies]
async-trait = "0.1.42"
avahi-sys = { git = "https://github.com/Start9Labs/avahi-sys", branch = "feature/dynamic-linking", features = ["dynamic"], optional = true }
avahi-sys = { git = "https://github.com/Start9Labs/avahi-sys", branch = "feature/dynamic-linking", features = [
"dynamic",
], optional = true }
base32 = "0.4.0"
clap = "2.33"
ctrlc = "3.1.7"

View File

@@ -30,8 +30,11 @@ mod v0_2_10;
mod v0_2_11;
mod v0_2_12;
mod v0_2_13;
mod v0_2_14;
mod v0_2_15;
mod v0_2_16;
pub use v0_2_13::Version as Current;
pub use v0_2_16::Version as Current;
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
@@ -57,6 +60,9 @@ enum Version {
V0_2_11(Wrapper<v0_2_11::Version>),
V0_2_12(Wrapper<v0_2_12::Version>),
V0_2_13(Wrapper<v0_2_13::Version>),
V0_2_14(Wrapper<v0_2_14::Version>),
V0_2_15(Wrapper<v0_2_15::Version>),
V0_2_16(Wrapper<v0_2_16::Version>),
Other(emver::Version),
}
@@ -172,6 +178,9 @@ pub async fn init() -> Result<(), failure::Error> {
Version::V0_2_11(v) => v.0.migrate_to(&Current::new()).await?,
Version::V0_2_12(v) => v.0.migrate_to(&Current::new()).await?,
Version::V0_2_13(v) => v.0.migrate_to(&Current::new()).await?,
Version::V0_2_14(v) => v.0.migrate_to(&Current::new()).await?,
Version::V0_2_15(v) => v.0.migrate_to(&Current::new()).await?,
Version::V0_2_16(v) => v.0.migrate_to(&Current::new()).await?,
Version::Other(_) => (),
// TODO find some way to automate this?
}
@@ -266,6 +275,9 @@ pub async fn self_update(requirement: emver::VersionRange) -> Result<(), Error>
Version::V0_2_11(v) => Current::new().migrate_to(&v.0).await?,
Version::V0_2_12(v) => Current::new().migrate_to(&v.0).await?,
Version::V0_2_13(v) => Current::new().migrate_to(&v.0).await?,
Version::V0_2_14(v) => Current::new().migrate_to(&v.0).await?,
Version::V0_2_15(v) => Current::new().migrate_to(&v.0).await?,
Version::V0_2_16(v) => Current::new().migrate_to(&v.0).await?,
Version::Other(_) => (),
// TODO find some way to automate this?
};

View File

@@ -0,0 +1,21 @@
use super::*;
const V0_2_14: emver::Version = emver::Version::new(0, 2, 14, 0);
pub struct Version;
#[async_trait]
impl VersionT for Version {
type Previous = v0_2_13::Version;
fn new() -> Self {
Version
}
fn semver(&self) -> &'static emver::Version {
&V0_2_14
}
async fn up(&self) -> Result<(), Error> {
Ok(())
}
async fn down(&self) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -0,0 +1,21 @@
use super::*;
const V0_2_15: emver::Version = emver::Version::new(0, 2, 15, 0);
pub struct Version;
#[async_trait]
impl VersionT for Version {
type Previous = v0_2_14::Version;
fn new() -> Self {
Version
}
fn semver(&self) -> &'static emver::Version {
&V0_2_15
}
async fn up(&self) -> Result<(), Error> {
Ok(())
}
async fn down(&self) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -0,0 +1,21 @@
use super::*;
const V0_2_16: emver::Version = emver::Version::new(0, 2, 16, 0);
pub struct Version;
#[async_trait]
impl VersionT for Version {
type Previous = v0_2_15::Version;
fn new() -> Self {
Version
}
fn semver(&self) -> &'static emver::Version {
&V0_2_16
}
async fn up(&self) -> Result<(), Error> {
Ok(())
}
async fn down(&self) -> Result<(), Error> {
Ok(())
}
}

View File

@@ -1,6 +1,6 @@
manifest-version: 0
app-id: start9-ambassador
app-version: 0.2.13
app-version: 0.2.17
uri-rewrites:
- =/api -> http://{{start9-ambassador}}:5959/authenticate
- /api/ -> http://{{start9-ambassador}}:5959/

19744
ui/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "embassy-ui",
"version": "0.2.13",
"version": "0.2.17",
"description": "GUI for EmbassyOS",
"author": "Start9 Labs",
"homepage": "https://github.com/Start9Labs/embassy-ui",
@@ -53,7 +53,7 @@
"@types/marked": "^1.1.0",
"@types/node": "^14.11.10",
"@types/uuid": "^8.0.0",
"node-html-parser": "^2.0.0",
"node-html-parser": "2.0.0",
"ts-node": "^9.1.0",
"tslint": "^6.1.0",
"typescript": "4.0.5"

View File

@@ -298,6 +298,7 @@ export class ConfigCursor<T extends ValueType> {
const mappedCfg = this.mappedConfig()
if (cfg && mappedCfg && typeof cfg === 'object' && typeof mappedCfg === 'object') {
const spec = this.spec()
if (spec === undefined) return true
let allKeys: Set<string>
if (spec.type === 'union') {
let unionSpec = spec as ValueSpecOf<'union'>
@@ -482,4 +483,4 @@ export function displayUniqueBy(uniqueBy: UniqueBy, spec: ValueSpecObject | Valu
}
}).join(' or ')
}
}
}

View File

@@ -1,7 +1,7 @@
<ion-header>
<ion-toolbar>
<ion-title >
<ion-label style="font-size: 20px;" class="ion-text-wrap">Welcome to 0.2.13!</ion-label>
<ion-label style="font-size: 20px;" class="ion-text-wrap">Welcome to 0.2.17!</ion-label>
</ion-title>
</ion-toolbar>
</ion-header>
@@ -10,8 +10,7 @@
<div style="display: flex; flex-direction: column; justify-content: space-between; height: 100%">
<h2>Highlights</h2>
<div class="main-content">
<p>At long last, Matrix has arrived!</p>
<p>This release also enables displaying Service license information and contains utilities to facilitate the next major release of EmbassyOS.</p>
<p>This release fixes a bug with certificate generation that caused the Embassy web interface to become inaccessible</p>
</div>
<div class="close-button">

View File

@@ -499,8 +499,8 @@ const mockApiNotifications: ReqRes.GetNotificationsRes = [
const mockApiServer: () => ReqRes.GetServerRes = () => ({
serverId: 'start9-mockxyzab',
name: 'Embassy:12345678',
versionInstalled: '0.2.13',
versionLatest: '0.2.13',
versionInstalled: '0.2.17',
versionLatest: '0.2.17',
status: ServerStatus.RUNNING,
alternativeRegistryUrl: 'beta-registry.start9labs.com',
welcomeAck: true,