Repository: bitmaelum/bitmaelum-suite
Pull request #65 (2020-11-06 12:16:25)

Bm send

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [9 Code Smells](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='1.4%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list) [1.4% Duplication](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [9 Code Smells](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='1.4%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list) [1.4% Duplication](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list)


codecov[bot]: # [Codecov](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65?src=pr&el=h1) Report
> Merging [#65](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65?src=pr&el=desc) into [develop](https://codecov.io/gh/bitmaelum/bitmaelum-suite/commit/7a3227852a579b6efd21b317e07b9e367c2a3bfb?el=desc) will **increase** coverage by `9.93%`.
> The diff coverage is `64.44%`.

[![Impacted file tree graph](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/graphs/tree.svg?width=650&height=150&src=pr&token=8VR72NKYS0)](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65?src=pr&el=tree)

```diff
@@             Coverage Diff             @@
##           develop      #65      +/-   ##
===========================================
+ Coverage    48.43%   58.37%   +9.93%     
===========================================
  Files           92       83       -9     
  Lines         3491     2936     -555     
===========================================
+ Hits          1691     1714      +23     
+ Misses        1593     1010     -583     
- Partials       207      212       +5     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `58.37% <64.44%> (+9.93%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [cmd/bm-server/middleware/auth/auth\_onbehalf\_jwt.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-Y21kL2JtLXNlcnZlci9taWRkbGV3YXJlL2F1dGgvYXV0aF9vbmJlaGFsZl9qd3QuZ28=) | `61.11% <61.11%> (ø)` | |
| [internal/key/auth\_key.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-aW50ZXJuYWwva2V5L2F1dGhfa2V5Lmdv) | `86.66% <66.66%> (-13.34%)` | :arrow_down: |
| [internal/jwt.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-aW50ZXJuYWwvand0Lmdv) | `62.90% <100.00%> (ø)` | |
| [cmd/bm-client/handlers/fetch\_messages.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-Y21kL2JtLWNsaWVudC9oYW5kbGVycy9mZXRjaF9tZXNzYWdlcy5nbw==) | | |
| [cmd/bm-client/handlers/create\_account.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-Y21kL2JtLWNsaWVudC9oYW5kbGVycy9jcmVhdGVfYWNjb3VudC5nbw==) | | |
| [cmd/bm-client/handlers/create\_organisation.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-Y21kL2JtLWNsaWVudC9oYW5kbGVycy9jcmVhdGVfb3JnYW5pc2F0aW9uLmdv) | | |
| [cmd/bm-client/handlers/otp.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-Y21kL2JtLWNsaWVudC9oYW5kbGVycy9vdHAuZ28=) | | |
| [cmd/bm-client/handlers/list\_accounts.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-Y21kL2JtLWNsaWVudC9oYW5kbGVycy9saXN0X2FjY291bnRzLmdv) | | |
| [...d/bm-client/handlers/create\_organisation\_invite.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-Y21kL2JtLWNsaWVudC9oYW5kbGVycy9jcmVhdGVfb3JnYW5pc2F0aW9uX2ludml0ZS5nbw==) | | |
| [cmd/bm-client/handlers/read\_message.go](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree#diff-Y21kL2JtLWNsaWVudC9oYW5kbGVycy9yZWFkX21lc3NhZ2UuZ28=) | | |
| ... and [3 more](https://codecov.io/gh/bitmaelum/bitmaelum-suite/pull/65/diff?src=pr&el=tree-more) | |

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [14 Code Smells](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='1.3%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list) [1.3% Duplication](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [14 Code Smells](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/3.png' alt='1.2%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list) [1.2% Duplication](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: SonarCloud Quality Gate failed.

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [18 Code Smells](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/5.png' alt='3.8%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list) [3.8% Duplication](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: SonarCloud Quality Gate failed.

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug.png' alt='Bug' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=BUG)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability.png' alt='Vulnerability' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=VULNERABILITY) (and [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot.png' alt='Security Hotspot' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=SECURITY_HOTSPOT) to review)  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell.png' alt='Code Smell' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A.png' alt='A' width='16' height='16' />](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL) [0 Code Smells](https://sonarcloud.io/project/issues?id=bitmaelum_bitmaelum-suite&pullRequest=65&resolved=false&types=CODE_SMELL)

[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo.png' alt='No Coverage information' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65) No Coverage information  
[<img src='https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/5.png' alt='3.8%' width='16' height='16' />](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list) [3.8% Duplication](https://sonarcloud.io/component_measures?id=bitmaelum_bitmaelum-suite&pullRequest=65&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/.gitignore b/.gitignore
index 11ed6d9..151080d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,1 @@
 release/
-bm-data/
diff --git a/LICENSE b/LICENSE
index 5deeace..2fbe6d3 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License (MIT)
 
-Copyright © 2020 Joshua Thijssen
+Copyright © 2020 BitMaelum Authors
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile b/Makefile
index 75decf6..ca8b5bf 100644
--- a/Makefile
+++ b/Makefile
@@ -8,8 +8,8 @@ SHELL=/bin/bash -O globstar -c
 REPO="github.com/bitmaelum/bitmaelum-suite"
 
 # Our defined apps and tools
-APPS := bm-server bm-client bm-config bm-mail
-TOOLS := hash-address jwt proof-of-work readmail update-resolver resolve vault-edit resolve-auth update-pow jwt-validate migrate-hash check-org toaster
+APPS := bm-server bm-client bm-config bm-mail bm-send
+TOOLS := hash-address jwt proof-of-work readmail update-resolver resolve vault-edit resolve-auth update-pow jwt-validate check-org toaster
 
 # These files are checked for license headers
 LICENSE_CHECK_DIRS=internal/**/*.go pkg/**/*.go tools/**/*.go cmd/**/*.go
diff --git a/cmd/bm-client/cmd/api_create.go b/cmd/bm-client/cmd/api_create.go
index 47bb152..29a9767 100644
--- a/cmd/bm-client/cmd/api_create.go
+++ b/cmd/bm-client/cmd/api_create.go
@@ -24,9 +24,8 @@ import (
 	"os"
 	"time"
 
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/key"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/sirupsen/logrus"
@@ -41,7 +40,7 @@ var apiCreateCmd = &cobra.Command{
 		v := vault.OpenVault()
 		info := vault.GetAccountOrDefault(v, *apiAddress)
 
-		resolver := container.GetResolveService()
+		resolver := container.Instance.GetResolveService()
 		routingInfo, err := resolver.ResolveRouting(info.RoutingID)
 		if err != nil {
 			logrus.Fatal("Cannot find routing ID for this account")
@@ -59,17 +58,13 @@ var apiCreateCmd = &cobra.Command{
 		fmt.Printf("%#v\n", key)
 		fmt.Printf("%#v\n", *acValidUntil)
 
-		client, err := api.NewAuthenticated(info, api.ClientOpts{
-			Host:          routingInfo.Routing,
-			AllowInsecure: config.Client.Server.AllowInsecure,
-			Debug:         config.Client.Server.DebugHTTP,
-		})
+		client, err := api.NewAuthenticated(info.Address, &info.PrivKey, routingInfo.Routing)
 		if err != nil {
 			logrus.Fatal(err)
 			os.Exit(1)
 		}
 
-		err = client.CreateAPIKey(info.AddressHash(), key)
+		err = client.CreateAPIKey(info.Address.Hash(), key)
 		if err != nil {
 			logrus.Fatal(err)
 			os.Exit(1)
diff --git a/cmd/bm-client/cmd/api_list.go b/cmd/bm-client/cmd/api_list.go
index 9db484c..aa5d8cf 100644
--- a/cmd/bm-client/cmd/api_list.go
+++ b/cmd/bm-client/cmd/api_list.go
@@ -24,9 +24,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/olekukonko/tablewriter"
 	"github.com/sirupsen/logrus"
@@ -41,24 +40,20 @@ var apiListCmd = &cobra.Command{
 		v := vault.OpenVault()
 		info := vault.GetAccountOrDefault(v, *apiAddress)
 
-		resolver := container.GetResolveService()
+		resolver := container.Instance.GetResolveService()
 		routingInfo, err := resolver.ResolveRouting(info.RoutingID)
 		if err != nil {
 			logrus.Fatal("Cannot find routing ID for this account")
 			os.Exit(1)
 		}
 
-		client, err := api.NewAuthenticated(info, api.ClientOpts{
-			Host:          routingInfo.Routing,
-			AllowInsecure: config.Client.Server.AllowInsecure,
-			Debug:         config.Client.Server.DebugHTTP,
-		})
+		client, err := api.NewAuthenticated(info.Address, &info.PrivKey, routingInfo.Routing)
 		if err != nil {
 			logrus.Fatal(err)
 			os.Exit(1)
 		}
 
-		keys, err := client.ListAPIKeys(info.AddressHash())
+		keys, err := client.ListAPIKeys(info.Address.Hash())
 		if err != nil {
 			logrus.Fatal(err)
 			os.Exit(1)
diff --git a/cmd/bm-client/cmd/auth_list.go b/cmd/bm-client/cmd/auth_list.go
index 4ae87bc..d05706a 100644
--- a/cmd/bm-client/cmd/auth_list.go
+++ b/cmd/bm-client/cmd/auth_list.go
@@ -23,9 +23,8 @@ import (
 	"os"
 	"time"
 
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/olekukonko/tablewriter"
 	"github.com/sirupsen/logrus"
@@ -41,24 +40,20 @@ var authListCmd = &cobra.Command{
 		v := vault.OpenVault()
 		info := vault.GetAccountOrDefault(v, *authAccount)
 
-		resolver := container.GetResolveService()
+		resolver := container.Instance.GetResolveService()
 		routingInfo, err := resolver.ResolveRouting(info.RoutingID)
 		if err != nil {
 			logrus.Fatal("Cannot find routing ID for this account")
 			os.Exit(1)
 		}
 
-		client, err := api.NewAuthenticated(info, api.ClientOpts{
-			Host:          routingInfo.Routing,
-			AllowInsecure: config.Client.Server.AllowInsecure,
-			Debug:         config.Client.Server.DebugHTTP,
-		})
+		client, err := api.NewAuthenticated(info.Address, &info.PrivKey, routingInfo.Routing)
 		if err != nil {
 			logrus.Fatal(err)
 			os.Exit(1)
 		}
 
-		keys, err := client.ListAuthKeys(info.AddressHash())
+		keys, err := client.ListAuthKeys(info.Address.Hash())
 		if err != nil {
 			logrus.Fatal(err)
 			os.Exit(1)
diff --git a/cmd/bm-client/cmd/compose.go b/cmd/bm-client/cmd/compose.go
index fd5cb63..a68a3be 100644
--- a/cmd/bm-client/cmd/compose.go
+++ b/cmd/bm-client/cmd/compose.go
@@ -27,7 +27,9 @@ import (
 	"os/exec"
 
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/handlers"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
+	"github.com/bitmaelum/bitmaelum-suite/internal/message"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
 	"github.com/sirupsen/logrus"
@@ -85,7 +87,29 @@ var composeCmd = &cobra.Command{
 			fmt.Printf("  Att.   #%d %s\n", i, attachment)
 		}
 
-		err = handlers.ComposeMessage(*fromInfo, *toAddr, *subject, *blocks, *attachments)
+		// Resolve all stuff
+		resolver := container.Instance.GetResolveService()
+		routingInfo, err := resolver.ResolveRouting(fromInfo.RoutingID)
+		if err != nil {
+			logrus.Fatal("Cannot find routing ID for this account")
+			os.Exit(1)
+		}
+
+		recipientInfo, err := resolver.ResolveAddress(toAddr.Hash())
+		if err != nil {
+			logrus.Fatal("Cannot resolve recipient")
+			os.Exit(1)
+		}
+
+		// Setup addressing and compose the message
+		addressing := message.NewAddressing(
+			fromInfo.Address,
+			&fromInfo.PrivKey,
+			routingInfo.Routing,
+			*toAddr,
+			&recipientInfo.PublicKey,
+		)
+		err = handlers.ComposeMessage(addressing, *subject, *blocks, *attachments)
 		if err != nil {
 			logrus.Fatal(err)
 		}
diff --git a/cmd/bm-client/cmd/create_account.go b/cmd/bm-client/cmd/create_account.go
index e62c45f..74e707e 100644
--- a/cmd/bm-client/cmd/create_account.go
+++ b/cmd/bm-client/cmd/create_account.go
@@ -40,7 +40,6 @@ This assumes you have a BitMaelum invitation token for the specific server.`,
 }
 
 var addr, name, token, keytype *string
-var rsa *bool
 
 func init() {
 	rootCmd.AddCommand(createAccountCmd)
diff --git a/cmd/bm-client/cmd/read.go b/cmd/bm-client/cmd/read.go
index 7ef53a4..319495d 100644
--- a/cmd/bm-client/cmd/read.go
+++ b/cmd/bm-client/cmd/read.go
@@ -23,7 +23,7 @@ import (
 	"os"
 
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/handlers"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/sirupsen/logrus"
 
@@ -46,14 +46,14 @@ var readCmd = &cobra.Command{
 		}
 
 		// Fetch routing info
-		resolver := container.GetResolveService()
+		resolver := container.Instance.GetResolveService()
 		routingInfo, err := resolver.ResolveRouting(info.RoutingID)
 		if err != nil {
 			logrus.Fatal("Cannot find routing ID for this account")
 			os.Exit(1)
 		}
 
-		handlers.ReadMessage(info, routingInfo, *rBox, *rMessageID, *rBlock)
+		handlers.ReadMessage(info, routingInfo, *rBox, *rMessageID)
 	},
 }
 
@@ -61,7 +61,6 @@ var (
 	rAccount   *string
 	rBox       *string
 	rMessageID *string
-	rBlock     *string
 )
 
 func init() {
@@ -70,5 +69,4 @@ func init() {
 	rAccount = readCmd.Flags().StringP("account", "a", "", "Account")
 	rBox = readCmd.Flags().StringP("box", "b", "", "Box to fetch")
 	rMessageID = readCmd.Flags().StringP("message", "m", "", "Message ID")
-	rBlock = readCmd.Flags().StringP("block", "", "default", "block")
 }
diff --git a/cmd/bm-client/cmd/set_default.go b/cmd/bm-client/cmd/set_default.go
index 430255d..b78d57f 100644
--- a/cmd/bm-client/cmd/set_default.go
+++ b/cmd/bm-client/cmd/set_default.go
@@ -47,7 +47,7 @@ var setDefaultCmd = &cobra.Command{
 		for i := range v.Store.Accounts {
 			v.Store.Accounts[i].Default = false
 
-			if v.Store.Accounts[i].Address == *sdFrom {
+			if v.Store.Accounts[i].Address.String() == *sdFrom {
 				v.Store.Accounts[i].Default = true
 			}
 		}
diff --git a/cmd/bm-client/handlers/auth_create.go b/cmd/bm-client/handlers/auth_create.go
index edbd976..601d8fc 100644
--- a/cmd/bm-client/handlers/auth_create.go
+++ b/cmd/bm-client/handlers/auth_create.go
@@ -23,23 +23,22 @@ import (
 	"errors"
 	"time"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/key"
+	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 )
 
 // CreateAuthorizedKey creates a new authorized key
-func CreateAuthorizedKey(info *internal.AccountInfo, targetKey *bmcrypto.PubKey, validUntil time.Duration, desc string) error {
+func CreateAuthorizedKey(info *vault.AccountInfo, targetKey *bmcrypto.PubKey, validUntil time.Duration, desc string) error {
 	var expiry = time.Time{}
 	if validUntil > 0 {
 		expiry = time.Now().Add(validUntil)
 	}
 
 	// Create and sign key
-	k := key.NewAuthKey(info.AddressHash(), targetKey, "", expiry, desc)
+	k := key.NewAuthKey(info.Address.Hash(), targetKey, "", expiry, desc)
 	err := k.Sign(info.PrivKey)
 	if err != nil {
 		return err
@@ -51,19 +50,15 @@ func CreateAuthorizedKey(info *internal.AccountInfo, targetKey *bmcrypto.PubKey,
 		return err
 	}
 
-	return client.CreateAuthKey(info.AddressHash(), k)
+	return client.CreateAuthKey(info.Address.Hash(), k)
 }
 
-func getAPIClient(info *internal.AccountInfo) (*api.API, error) {
-	resolver := container.GetResolveService()
+func getAPIClient(info *vault.AccountInfo) (*api.API, error) {
+	resolver := container.Instance.GetResolveService()
 	routingInfo, err := resolver.ResolveRouting(info.RoutingID)
 	if err != nil {
 		return nil, errors.New("cannot find routing ID for this account")
 	}
 
-	return api.NewAuthenticated(info, api.ClientOpts{
-		Host:          routingInfo.Routing,
-		AllowInsecure: config.Client.Server.AllowInsecure,
-		Debug:         config.Client.Server.DebugHTTP,
-	})
+	return api.NewAuthenticated(info.Address, &info.PrivKey, routingInfo.Routing)
 }
diff --git a/cmd/bm-client/handlers/compose.go b/cmd/bm-client/handlers/compose.go
index 9051336..6dab961 100644
--- a/cmd/bm-client/handlers/compose.go
+++ b/cmd/bm-client/handlers/compose.go
@@ -20,252 +20,28 @@
 package handlers
 
 import (
-	"bytes"
-	"errors"
-	"fmt"
-	"io"
-	"os"
-	"strings"
-
-	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
-	"github.com/bitmaelum/bitmaelum-suite/internal/client"
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
-	"github.com/bitmaelum/bitmaelum-suite/internal/encrypt"
 	"github.com/bitmaelum/bitmaelum-suite/internal/message"
-	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
-	"golang.org/x/sync/errgroup"
+	"github.com/bitmaelum/bitmaelum-suite/internal/messages"
 )
 
 // ComposeMessage composes a new message from the given account Info to the "to" with given subject, blocks and attachments
-func ComposeMessage(info internal.AccountInfo, toAddr address.Address, subject string, b, a []string) error {
-	// Resolve public key for our recipient
-	resolver := container.GetResolveService()
-	toInfo, err := resolver.ResolveAddress(toAddr.Hash())
-	if err != nil {
-		return fmt.Errorf("cannot retrieve public key for '%s'", toAddr.String())
-	}
-
-	// Generate blocks and attachments
-	blocks, err := generateBlocks(b)
-	if err != nil {
-		return err
-	}
-	attachments, err := generateAttachments(a)
-	if err != nil {
-		return err
-	}
-
-	// Generate catalog
-	catalog, err := generateCatalog(info, toAddr, subject, blocks, attachments)
-	if err != nil {
-		return err
-	}
-
-	// Encrypt catalog for upload
-	catalogKey, encryptedCatalog, err := encrypt.CatalogEncrypt(*catalog)
-	if err != nil {
-		return err
-	}
-
-	// Generate header based on our encrypted catalog
-	header, err := generateHeader(info, toInfo, encryptedCatalog, catalogKey)
+func ComposeMessage(addressing message.Addressing, subject string, b, a []string) error {
+	envelope, err := message.Compose(addressing, subject, b, a)
 	if err != nil {
 		return err
 	}
 
-	//Sign the header
-	err = client.SignHeader(header, info.PrivKey)
-	if err != nil {
-		return err
-	}
-
-	// Fetch routing info for the SENDER, as we send to the sender's mail server (not the recipient)
-	routingInfo, err := resolver.ResolveRouting(info.RoutingID)
+	// Setup API connection to the server
+	client, err := api.NewAuthenticated(addressing.Sender.Address, addressing.Sender.PrivKey, addressing.Sender.Host)
 	if err != nil {
 		return err
 	}
 
 	// and finally send
-	err = uploadToServer(info, *routingInfo, header, encryptedCatalog, catalog)
+	err = messages.Send(*client, envelope)
 	if err != nil {
 		return err
 	}
 	return nil
 }
-
-func uploadToServer(info internal.AccountInfo, routingInfo resolver.RoutingInfo, header *message.Header, encryptedCatalog []byte, catalog *message.Catalog) error {
-	client, err := api.NewAuthenticated(&info, api.ClientOpts{
-		Host:          routingInfo.Routing,
-		AllowInsecure: config.Client.Server.AllowInsecure,
-		Debug:         config.Client.Server.DebugHTTP,
-	})
-	if err != nil {
-		return err
-	}
-
-	// Get upload ticket
-	t, err := client.GetTicket(header.From.Addr, header.To.Addr, "")
-	if err != nil {
-		return errors.New("cannot get ticket from server: " + err.Error())
-	}
-	if !t.Valid {
-		return errors.New("invalid ticket returned by server")
-	}
-
-	// parallelize uploads
-	g := new(errgroup.Group)
-	g.Go(func() error {
-		return client.UploadHeader(*t, header)
-	})
-	g.Go(func() error {
-		return client.UploadCatalog(*t, encryptedCatalog)
-	})
-	for _, block := range catalog.Blocks {
-		// Store locally, otherwise the anonymous go function doesn't know which "block"
-		b := block
-		g.Go(func() error {
-			return client.UploadBlock(*t, b.ID, b.Reader)
-		})
-	}
-	for _, attachment := range catalog.Attachments {
-		// Store locally, otherwise the anonymous go function doesn't know which "attachment"
-		a := attachment
-		g.Go(func() error {
-			return client.UploadBlock(*t, a.ID, a.Reader)
-		})
-	}
-
-	// Wait until all are completed
-	if err := g.Wait(); err != nil {
-		_ = client.DeleteMessage(*t)
-		return err
-	}
-
-	// All done, mark upload as completed
-	return client.CompleteUpload(*t)
-}
-
-// Generate a header file based on the info provided
-func generateHeader(info internal.AccountInfo, toInfo *resolver.AddressInfo, catalog []byte, catalogKey []byte) (*message.Header, error) {
-	header := &message.Header{}
-
-	// We can add a multitude of checksums here.. whatever we like
-	r := bytes.NewBuffer(catalog)
-	var err error
-	header.Catalog.Checksum, err = message.CalculateChecksums(r)
-	if err != nil {
-		return nil, err
-	}
-	header.Catalog.Size = uint64(len(catalog))
-	header.Catalog.EncryptedKey, header.Catalog.TransactionID, header.Catalog.Crypto, err = bmcrypto.Encrypt(toInfo.PublicKey, catalogKey)
-	if err != nil {
-		return nil, err
-	}
-
-	h, err := hash.NewFromHash(toInfo.Hash)
-	if err != nil {
-		return nil, err
-	}
-	header.To.Addr = *h
-	header.From.Addr = info.AddressHash()
-
-	return header, nil
-}
-
-// Generate a complete catalog file. Outputs catalog key and the encrypted catalog
-func generateCatalog(info internal.AccountInfo, toAddr address.Address, subject string, b []message.Block, a []message.Attachment) (*message.Catalog, error) {
-	// Create catalog
-	cat := message.NewCatalog(&info)
-
-	cat.AddFlags("new")
-	cat.AddLabels("important")
-	cat.SetToAddress(toAddr, "John Doe")
-
-	cat.Subject = subject
-	cat.ThreadID = ""
-
-	for _, block := range b {
-		err := cat.AddBlock(block)
-		if err != nil {
-			return nil, err
-		}
-	}
-	for _, attachment := range a {
-		err := cat.AddAttachment(attachment)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	return cat, nil
-}
-
-// Generate message attachments based on the given paths to files
-func generateAttachments(a []string) ([]message.Attachment, error) {
-	// Parse attachments
-	var attachments []message.Attachment
-	for _, attachment := range a {
-		_, err := os.Stat(attachment)
-		if os.IsNotExist(err) {
-			return nil, fmt.Errorf("attachment %s does not exist", attachment)
-		}
-
-		reader, err := os.Open(attachment)
-		if err != nil {
-			return nil, fmt.Errorf("attachment %s cannot be opened", attachment)
-		}
-
-		attachments = append(attachments, message.Attachment{
-			Path:   attachment,
-			Reader: reader,
-		})
-	}
-
-	return attachments, nil
-}
-
-// Generate message blocks based on the given strings
-func generateBlocks(b []string) ([]message.Block, error) {
-	// Parse blocks
-	var blocks []message.Block
-	for _, block := range b {
-		split := strings.SplitN(block, ",", 2)
-		if len(split) <= 1 {
-			return nil, fmt.Errorf("please specify blocks in the format '<type>,<content>' or '<type>,file:<filename>'")
-		}
-
-		// By default assume content is inline
-		size := int64(len(split[1]))
-		var r io.Reader = strings.NewReader(split[1])
-
-		if strings.HasPrefix(split[1], "file:") {
-			// Open file as a reader
-			f, err := os.Open(strings.TrimPrefix(split[1], "file:"))
-			if err != nil {
-				return nil, err
-			}
-
-			// Read file size
-			fi, err := f.Stat()
-			if err != nil {
-				return nil, err
-			}
-
-			r = f
-			size = fi.Size()
-		}
-
-		blocks = append(blocks, message.Block{
-			Type:   split[0],
-			Size:   uint64(size),
-			Reader: r,
-		})
-	}
-
-	return blocks, nil
-}
diff --git a/cmd/bm-client/handlers/create_account.go b/cmd/bm-client/handlers/create_account.go
index 7778cc4..b56e7b7 100644
--- a/cmd/bm-client/handlers/create_account.go
+++ b/cmd/bm-client/handlers/create_account.go
@@ -23,12 +23,12 @@ import (
 	"fmt"
 	"os"
 
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
-	"github.com/bitmaelum/bitmaelum-suite/internal/invite"
 	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
+	"github.com/bitmaelum/bitmaelum-suite/internal/signature"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
@@ -54,7 +54,7 @@ func verifyAddress(bmAddr string) *address.Address {
 func checkAddressInResolver(addr address.Address) *resolver.Service {
 	fmt.Printf("* Checking if address is already known in the resolver service: ")
 
-	ks := container.GetResolveService()
+	ks := container.Instance.GetResolveService()
 	_, err := ks.ResolveAddress(addr.Hash())
 
 	if err == nil {
@@ -68,10 +68,10 @@ func checkAddressInResolver(addr address.Address) *resolver.Service {
 	return ks
 }
 
-func checkToken(token string, addr address.Address) *invite.Token {
+func checkToken(token string, addr address.Address) *signature.Token {
 	fmt.Printf("* Checking token format and extracting data: ")
 
-	it, err := invite.ParseInviteToken(token)
+	it, err := signature.ParseInviteToken(token)
 	if err != nil {
 		fmt.Println("\n  X it seems that this token is invalid")
 		os.Exit(1)
@@ -88,7 +88,7 @@ func checkToken(token string, addr address.Address) *invite.Token {
 	return it
 }
 
-func checkAccountInVault(vault *vault.Vault, addr address.Address) *internal.AccountInfo {
+func checkAccountInVault(vault *vault.Vault, addr address.Address) *vault.AccountInfo {
 	fmt.Printf("* Checking if the account is already present in the vault: ")
 
 	if vault.HasAccount(addr) {
@@ -107,7 +107,7 @@ func checkAccountInVault(vault *vault.Vault, addr address.Address) *internal.Acc
 }
 
 // CreateAccount creates a new account locally in the vault, stores it on the mail server and pushes the public key to the resolver
-func CreateAccount(vault *vault.Vault, bmAddr, name, token string, keyType bmcrypto.KeyType) {
+func CreateAccount(v *vault.Vault, bmAddr, name, token string, keyType bmcrypto.KeyType) {
 	var mnemonicToShow string
 
 	fmt.Println("")
@@ -115,7 +115,7 @@ func CreateAccount(vault *vault.Vault, bmAddr, name, token string, keyType bmcry
 	addr := verifyAddress(bmAddr)
 	ks := checkAddressInResolver(*addr)
 	it := checkToken(token, *addr)
-	info := checkAccountInVault(vault, *addr)
+	info := checkAccountInVault(v, *addr)
 
 	if info == nil {
 		fmt.Printf("* Generating your secret key to send and read mail: ")
@@ -136,8 +136,8 @@ func CreateAccount(vault *vault.Vault, bmAddr, name, token string, keyType bmcry
 		fmt.Printf("done.\n")
 
 		fmt.Printf("* Adding your new account into the vault: ")
-		info = &internal.AccountInfo{
-			Address:   bmAddr,
+		info = &vault.AccountInfo{
+			Address:   *addr,
 			Name:      name,
 			PrivKey:   *privKey,
 			PubKey:    *pubKey,
@@ -145,8 +145,8 @@ func CreateAccount(vault *vault.Vault, bmAddr, name, token string, keyType bmcry
 			RoutingID: it.RoutingID, // Fetch from token
 		}
 
-		vault.AddAccount(*info)
-		err = vault.WriteToDisk()
+		v.AddAccount(*info)
+		err = v.WriteToDisk()
 		if err != nil {
 			fmt.Printf("\n  X error while saving account into vault: %#v", err)
 			fmt.Println("")
@@ -164,15 +164,11 @@ func CreateAccount(vault *vault.Vault, bmAddr, name, token string, keyType bmcry
 	}
 
 	fmt.Printf("* Sending your account information to the server: ")
-	client, err := api.NewAuthenticated(info, api.ClientOpts{
-		Host:          routingInfo.Routing,
-		AllowInsecure: config.Client.Server.AllowInsecure,
-		Debug:         config.Client.Server.DebugHTTP,
-	})
+	client, err := api.NewAuthenticated(info.Address, &info.PrivKey, routingInfo.Routing)
 	if err != nil {
 		// Remove account from the local vault as well, as we could not store on the server
-		vault.RemoveAccount(*addr)
-		_ = vault.WriteToDisk()
+		v.RemoveAccount(*addr)
+		_ = v.WriteToDisk()
 
 		fmt.Printf("cannot initialize API")
 		fmt.Println("")
@@ -182,8 +178,8 @@ func CreateAccount(vault *vault.Vault, bmAddr, name, token string, keyType bmcry
 	err = client.CreateAccount(*info, token)
 	if err != nil {
 		// Remove account from the local vault as well, as we could not store on the server
-		vault.RemoveAccount(*addr)
-		_ = vault.WriteToDisk()
+		v.RemoveAccount(*addr)
+		_ = v.WriteToDisk()
 
 		fmt.Printf("\n  X error from API while trying to create account: " + err.Error())
 		fmt.Println("")
diff --git a/cmd/bm-client/handlers/create_organisation.go b/cmd/bm-client/handlers/create_organisation.go
index 95a2d16..4bfcf80 100644
--- a/cmd/bm-client/handlers/create_organisation.go
+++ b/cmd/bm-client/handlers/create_organisation.go
@@ -23,9 +23,9 @@ import (
 	"fmt"
 	"os"
 
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/organisation"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
@@ -34,7 +34,7 @@ import (
 )
 
 // CreateOrganisation creates a new organisation locally in the vault and pushes the public key to the resolver
-func CreateOrganisation(vault *vault.Vault, orgAddr, fullName string, orgValidations []string, keyType bmcrypto.KeyType) {
+func CreateOrganisation(v *vault.Vault, orgAddr, fullName string, orgValidations []string, keyType bmcrypto.KeyType) {
 	fmt.Printf("* Verifying if organisation name is valid: ")
 	orgHash := hash.New(orgAddr)
 
@@ -48,7 +48,7 @@ func CreateOrganisation(vault *vault.Vault, orgAddr, fullName string, orgValidat
 	fmt.Printf("ok.\n")
 
 	fmt.Printf("* Checking if organisation is already known in the resolver service: ")
-	ks := container.GetResolveService()
+	ks := container.Instance.GetResolveService()
 	_, err = ks.ResolveOrganisation(orgHash)
 	if err == nil {
 		fmt.Printf("\n  X it seems that this organisation is already in use. Please specify another organisation.")
@@ -60,8 +60,8 @@ func CreateOrganisation(vault *vault.Vault, orgAddr, fullName string, orgValidat
 	var mnemonic string
 
 	fmt.Printf("* Checking if the organisation is already present in the vault: ")
-	var info *internal.OrganisationInfo
-	if vault.HasOrganisation(orgHash) {
+	var info *vault.OrganisationInfo
+	if v.HasOrganisation(orgHash) {
 		fmt.Printf("\n  X organisation already present in the vault.\n")
 		fmt.Println("")
 		os.Exit(1)
@@ -91,7 +91,7 @@ func CreateOrganisation(vault *vault.Vault, orgAddr, fullName string, orgValidat
 		fmt.Printf("done.\n")
 
 		fmt.Printf("* Adding your new organisation into the vault: ")
-		info = &internal.OrganisationInfo{
+		info = &vault.OrganisationInfo{
 			Addr:        orgAddr,
 			FullName:    fullName,
 			PrivKey:     *privKey,
@@ -100,8 +100,8 @@ func CreateOrganisation(vault *vault.Vault, orgAddr, fullName string, orgValidat
 			Validations: val,
 		}
 
-		vault.AddOrganisation(*info)
-		err = vault.WriteToDisk()
+		v.AddOrganisation(*info)
+		err = v.WriteToDisk()
 		if err != nil {
 			fmt.Printf("\n  X error while saving organisation into vault: %#v", err)
 			fmt.Println("")
diff --git a/cmd/bm-client/handlers/create_organisation_invite.go b/cmd/bm-client/handlers/create_organisation_invite.go
index 4ad05ab..cf8ec59 100644
--- a/cmd/bm-client/handlers/create_organisation_invite.go
+++ b/cmd/bm-client/handlers/create_organisation_invite.go
@@ -24,8 +24,8 @@ import (
 	"os"
 	"time"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
-	"github.com/bitmaelum/bitmaelum-suite/internal/invite"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/internal/signature"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
@@ -46,7 +46,7 @@ func CreateOrganisationInvite(vault *vault.Vault, orgAddr, inviteAddr, shortRout
 	fmt.Println("Found Routing ID: ", routingID)
 
 	// Verify the routing ID exists
-	svc := container.GetResolveService()
+	svc := container.Instance.GetResolveService()
 	_, err = svc.ResolveRouting(routingID)
 	if err != nil {
 		fmt.Println("Cannot find the specified routing ID on the resolver")
@@ -69,7 +69,7 @@ func CreateOrganisationInvite(vault *vault.Vault, orgAddr, inviteAddr, shortRout
 	}
 
 	validUntil := time.Now().Add(7 * 24 * time.Hour)
-	token, err := invite.NewInviteToken(hashAddr.Hash(), routingID, validUntil, oi.PrivKey)
+	token, err := signature.NewInviteToken(hashAddr.Hash(), routingID, validUntil, oi.PrivKey)
 	if err != nil {
 		fmt.Println("Error while generating token: ", err)
 		os.Exit(1)
diff --git a/cmd/bm-client/handlers/fetch_messages.go b/cmd/bm-client/handlers/fetch_messages.go
index 87c1fdc..7b37f93 100644
--- a/cmd/bm-client/handlers/fetch_messages.go
+++ b/cmd/bm-client/handlers/fetch_messages.go
@@ -25,50 +25,44 @@ import (
 	"strings"
 	"time"
 
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
-	"github.com/bitmaelum/bitmaelum-suite/internal/encrypt"
 	"github.com/bitmaelum/bitmaelum-suite/internal/message"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
+	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/c2h5oh/datasize"
 	"github.com/olekukonko/tablewriter"
 )
 
 // FetchMessages will display message information from accounts and boxes
-func FetchMessages(accounts []internal.AccountInfo) {
+func FetchMessages(accounts []vault.AccountInfo) {
 	table := tablewriter.NewWriter(os.Stdout)
-	//table.SetAutoMergeCells(true)
+	// table.SetAutoMergeCells(true)
 
 	headers := []string{"Account", "Box", "ID", "Subject", "From", "Date", "# Blocks", "# Attachments"}
 	table.SetHeader(headers)
 
-	for _, account := range accounts {
+	for _, info := range accounts {
 		// Fetch routing info
-		resolver := container.GetResolveService()
-		routingInfo, err := resolver.ResolveRouting(account.RoutingID)
+		resolver := container.Instance.GetResolveService()
+		routingInfo, err := resolver.ResolveRouting(info.RoutingID)
 		if err != nil {
 			continue
 		}
 
-		client, err := api.NewAuthenticated(&account, api.ClientOpts{
-			Host:          routingInfo.Routing,
-			AllowInsecure: config.Client.Server.AllowInsecure,
-			Debug:         config.Client.Server.DebugHTTP,
-		})
+		client, err := api.NewAuthenticated(info.Address, &info.PrivKey, routingInfo.Routing)
 		if err != nil {
 			continue
 		}
 
-		displayBoxList(client, &account, table)
+		displayBoxList(client, &info, table)
 	}
 
 	table.Render()
 }
 
-func displayBoxList(client *api.API, account *internal.AccountInfo, table *tablewriter.Table) {
-	mbl, err := client.GetMailboxList(account.AddressHash())
+func displayBoxList(client *api.API, account *vault.AccountInfo, table *tablewriter.Table) {
+	mbl, err := client.GetMailboxList(account.Address.Hash())
 	if err != nil {
 		return
 	}
@@ -78,15 +72,15 @@ func displayBoxList(client *api.API, account *internal.AccountInfo, table *table
 	}
 }
 
-func displayBox(client *api.API, account *internal.AccountInfo, box string, table *tablewriter.Table) {
-	mb, err := client.GetMailboxMessages(account.AddressHash(), box)
+func displayBox(client *api.API, account *vault.AccountInfo, box string, table *tablewriter.Table) {
+	mb, err := client.GetMailboxMessages(account.Address.Hash(), box)
 	if err != nil {
 		return
 	}
 
 	if box == "1" {
 		values := []string{
-			account.Address,
+			account.Address.String(),
 			box,
 			"", "", "", "", "", "",
 		}
@@ -101,12 +95,12 @@ func displayBox(client *api.API, account *internal.AccountInfo, box string, tabl
 	}
 
 	for _, msg := range mb.Messages {
-		key, err := bmcrypto.Decrypt(account.PrivKey, msg.Header.Catalog.TransactionID, msg.Header.Catalog.EncryptedKey)
+		key, err := message.Decrypt(account.PrivKey, msg.Header.Catalog.TransactionID, msg.Header.Catalog.EncryptedKey)
 		if err != nil {
 			continue
 		}
 		catalog := &message.Catalog{}
-		err = encrypt.CatalogDecrypt(key, msg.Catalog, catalog)
+		err = internal.CatalogDecrypt(key, msg.Catalog, catalog)
 		if err != nil {
 			continue
 		}
diff --git a/cmd/bm-client/handlers/list_accounts.go b/cmd/bm-client/handlers/list_accounts.go
index 1934345..3909134 100644
--- a/cmd/bm-client/handlers/list_accounts.go
+++ b/cmd/bm-client/handlers/list_accounts.go
@@ -22,7 +22,7 @@ package handlers
 import (
 	"os"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/olekukonko/tablewriter"
 )
@@ -44,12 +44,13 @@ func ListAccounts(v *vault.Vault, displayKeys bool) {
 	table.SetHeader(headers)
 
 	for _, acc := range v.Store.Accounts {
+		suffix := ""
 		if acc.Default {
-			acc.Address += " (*)"
+			suffix = " (*)"
 		}
 
 		var route string
-		resolver := container.GetResolveService()
+		resolver := container.Instance.GetResolveService()
 		r, err := resolver.ResolveRouting(acc.RoutingID)
 		if err != nil {
 			route = "route not found"
@@ -58,7 +59,7 @@ func ListAccounts(v *vault.Vault, displayKeys bool) {
 		}
 
 		values := []string{
-			acc.Address,
+			acc.Address.String() + suffix,
 			acc.Name,
 			acc.RoutingID[:10],
 			route,
diff --git a/cmd/bm-client/handlers/list_organisations.go b/cmd/bm-client/handlers/list_organisations.go
index 456f5e1..fec0bed 100644
--- a/cmd/bm-client/handlers/list_organisations.go
+++ b/cmd/bm-client/handlers/list_organisations.go
@@ -22,7 +22,6 @@ package handlers
 import (
 	"os"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/olekukonko/tablewriter"
 )
@@ -44,17 +43,13 @@ func ListOrganisations(v *vault.Vault, displayKeys bool) {
 	table.SetHeader(headers)
 
 	for _, org := range v.Store.Organisations {
-		o, err := internal.InfoToOrg(org)
-		if err != nil {
-			continue
-		}
 
 		if len(org.Validations) == 0 {
 			values := []string{
 				"...@" + org.Addr + "!",
 				org.FullName,
 				"-",
-				o.Hash.String(),
+				org.ToOrg().Hash.String(),
 			}
 			if displayKeys {
 				values = append(values, org.PrivKey.S, org.PubKey.S)
@@ -64,11 +59,11 @@ func ListOrganisations(v *vault.Vault, displayKeys bool) {
 		}
 
 		for i, val := range org.Validations {
-			var valstr string
-			if ok, err := val.Validate(*o); err == nil && ok {
-				valstr = "\U00002713 " + val.String()
+			var valStr string
+			if ok, err := val.Validate(*org.ToOrg()); err == nil && ok {
+				valStr = "\U00002713 " + val.String()
 			} else {
-				valstr = "\U00002717 " + val.String()
+				valStr = "\U00002717 " + val.String()
 			}
 
 			var values []string
@@ -77,15 +72,15 @@ func ListOrganisations(v *vault.Vault, displayKeys bool) {
 				values = []string{
 					"...@" + org.Addr + "!",
 					org.FullName,
-					valstr,
-					o.Hash.String(),
+					valStr,
+					org.ToOrg().Hash.String(),
 				}
 			} else {
 				// Additional validation rows
 				values = []string{
 					"",
 					"",
-					valstr,
+					valStr,
 					"",
 				}
 			}
diff --git a/cmd/bm-client/handlers/otp.go b/cmd/bm-client/handlers/otp.go
index 1fb5a36..09db04c 100644
--- a/cmd/bm-client/handlers/otp.go
+++ b/cmd/bm-client/handlers/otp.go
@@ -31,8 +31,8 @@ import (
 	"strings"
 	"time"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-client/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/olekukonko/tablewriter"
@@ -42,7 +42,7 @@ import (
 const blockPeriod = 30
 
 // OtpGenerate will generate an OTP valid for otpServer
-func OtpGenerate(info *internal.AccountInfo, otpServer *string) {
+func OtpGenerate(info *vault.AccountInfo, otpServer *string) {
 	// Get
 	recs, err := net.LookupTXT("_bitmaelum." + *otpServer)
 	if err != nil {
@@ -50,7 +50,7 @@ func OtpGenerate(info *internal.AccountInfo, otpServer *string) {
 		logrus.Fatal(err)
 	}
 
-	resolver := container.GetResolveService()
+	resolver := container.Instance.GetResolveService()
 
 	for _, txt := range recs {
 		orgHash, err := hash.NewFromHash(strings.ToLower(txt))
diff --git a/cmd/bm-client/handlers/read_message.go b/cmd/bm-client/handlers/read_message.go
index 94f38fa..bb9044f 100644
--- a/cmd/bm-client/handlers/read_message.go
+++ b/cmd/bm-client/handlers/read_message.go
@@ -26,90 +26,43 @@ import (
 
 	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
-	clientSig "github.com/bitmaelum/bitmaelum-suite/internal/client"
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/encrypt"
 	"github.com/bitmaelum/bitmaelum-suite/internal/message"
 	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
+	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/c2h5oh/datasize"
 	"github.com/sirupsen/logrus"
 )
 
 // ReadMessage will read a specific message blocks
-func ReadMessage(info *internal.AccountInfo, routingInfo *resolver.RoutingInfo, box, messageID, blockType string) {
-	client, err := api.NewAuthenticated(info, api.ClientOpts{
-		Host:          routingInfo.Routing,
-		AllowInsecure: config.Client.Server.AllowInsecure,
-		Debug:         config.Client.Server.DebugHTTP,
-	})
+func ReadMessage(info *vault.AccountInfo, routingInfo *resolver.RoutingInfo, box, messageID string) {
+	client, err := api.NewAuthenticated(info.Address, &info.PrivKey, routingInfo.Routing)
 	if err != nil {
 		logrus.Fatal(err)
 	}
 
 	// Fetch message from API
-	msg, err := client.GetMessage(info.AddressHash(), box, messageID)
+	msg, err := client.GetMessage(info.Address.Hash(), box, messageID)
 	if err != nil {
 		logrus.Fatal(err)
 	}
 
-	key, err := bmcrypto.Decrypt(info.PrivKey, msg.Header.Catalog.TransactionID, msg.Header.Catalog.EncryptedKey)
+	key, err := message.Decrypt(info.PrivKey, msg.Header.Catalog.TransactionID, msg.Header.Catalog.EncryptedKey)
 	if err != nil {
 		logrus.Fatal(err)
 	}
 
 	// Verify the clientSignature
-	if !clientSig.VerifyHeader(msg.Header) {
+	if !message.VerifyClientHeader(msg.Header) {
 		logrus.Fatalf("message %s has failed the client signature check. Seems that this message may have been spoofed.", messageID)
 	}
 
 	// Decrypt the catalog
 	catalog := &message.Catalog{}
-	err = encrypt.CatalogDecrypt(key, msg.Catalog, catalog)
+	err = internal.CatalogDecrypt(key, msg.Catalog, catalog)
 	if err != nil {
 		logrus.Fatal(err)
 	}
 
-	// spew.Dump(catalog)
-	//
-	// for _, b := range catalog.Blocks {
-	// 	data, err := client.GetMessageBlock(*addr, box, messageID, b.ID)
-	// 	bb := bytes.NewBuffer(data)
-	//
-	// 	r, err := encrypt.GetAesDecryptorReader(b.IV, b.Key, bb)
-	// 	if err != nil {
-	// 		panic(err)
-	// 	}
-	//
-	// 	content, err := ioutil.ReadAll(r)
-	// 	if err != nil {
-	// 		logrus.Fatal(err)
-	// 	}
-	//
-	// 	spew.Dump(b)
-	// 	spew.Dump(content)
-	// }
-	//
-	// for _, a := range catalog.Attachments {
-	// 	ar, err := client.GetMessageAttachment(*addr, box, messageID, a.ID)
-	// 	if err != nil {
-	// 		panic(err)
-	// 	}
-	//
-	// 	r, err := encrypt.GetAesDecryptorReader(a.IV, a.Key, ar)
-	// 	if err != nil {
-	// 		panic(err)
-	// 	}
-	//
-	// 	content, err := ioutil.ReadAll(r)
-	// 	if err != nil {
-	// 		logrus.Fatal(err)
-	// 	}
-	//
-	// 	spew.Dump(a)
-	// 	spew.Dump(content)
-	// }
-
 	fmt.Printf("--------------------------------------------------------\n")
 	fmt.Printf("From       : %s <%s>\n", catalog.From.Name, catalog.From.Address)
 	fmt.Printf("To         : %s <%s>\n", catalog.To.Name, catalog.To.Address)
@@ -125,13 +78,13 @@ func ReadMessage(info *internal.AccountInfo, routingInfo *resolver.RoutingInfo,
 		fmt.Printf("Block %02d: %-20s %8s\n", idx, b.Type, datasize.ByteSize(b.Size))
 		fmt.Printf("\n")
 
-		data, err := client.GetMessageBlock(info.AddressHash(), box, messageID, b.ID)
+		data, err := client.GetMessageBlock(info.Address.Hash(), box, messageID, b.ID)
 		if err != nil {
 			panic(err)
 		}
 		bb := bytes.NewBuffer(data)
 
-		r, err := encrypt.GetAesDecryptorReader(b.IV, b.Key, bb)
+		r, err := internal.GetAesDecryptorReader(b.IV, b.Key, bb)
 		if err != nil {
 			panic(err)
 		}
diff --git a/cmd/bm-client/internal/container/container.go b/cmd/bm-client/internal/container/container.go
new file mode 100644
index 0000000..f22b411
--- /dev/null
+++ b/cmd/bm-client/internal/container/container.go
@@ -0,0 +1,105 @@
+// Copyright (c) 2020 BitMaelum Authors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+package container
+
+import (
+	maincontainer "github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/internal/key"
+	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
+	"github.com/bitmaelum/bitmaelum-suite/internal/subscription"
+	"github.com/bitmaelum/bitmaelum-suite/internal/ticket"
+)
+
+/*
+ * For more information about MultiContainers, please see:
+ *    github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container/container.go
+ */
+
+// MultiContainer is a struct that holds both the general and client container
+type MultiContainer struct {
+	general maincontainer.Container // The general container used in the internal packages
+	client  maincontainer.Container // The client container used in this command
+}
+
+// Instance is the main bitmaelum service container
+var Instance = MultiContainer{
+	general: &maincontainer.Instance,
+	client:  maincontainer.NewContainer(),
+}
+
+// SetShared will set a definition inside the client container
+func (c *MultiContainer) SetShared(key string, f maincontainer.ServiceFunc) {
+	c.client.SetShared(key, f)
+}
+
+// SetNonShared will set a definition inside the client container
+func (c *MultiContainer) SetNonShared(key string, f maincontainer.ServiceFunc) {
+	c.client.SetNonShared(key, f)
+}
+
+// Get will fetch a definition from the client container
+func (c *MultiContainer) Get(key string) interface{} {
+	return c.client.Get(key)
+}
+
+// GetAPIKeyRepo will return the current api key repository
+func (c *MultiContainer) GetAPIKeyRepo() key.APIKeyRepo {
+	if c.client.Has("api-key") {
+		return c.client.Get("api-key").(key.APIKeyRepo)
+	}
+
+	return c.general.Get("api-key").(key.APIKeyRepo)
+}
+
+// GetAuthKeyRepo will return the current auth key repository
+func (c *MultiContainer) GetAuthKeyRepo() key.AuthKeyRepo {
+	if c.client.Has("auth-key") {
+		return c.client.Get("auth-key").(key.AuthKeyRepo)
+	}
+
+	return c.general.Get("auth-key").(key.AuthKeyRepo)
+}
+
+// GetResolveService will return the current resolver service
+func (c *MultiContainer) GetResolveService() *resolver.Service {
+	if c.client.Has("resolver") {
+		return c.client.Get("resolver").(*resolver.Service)
+	}
+
+	return c.general.Get("resolver").(*resolver.Service)
+}
+
+// GetSubscriptionRepo will return the current subscription repository
+func (c *MultiContainer) GetSubscriptionRepo() subscription.Repository {
+	if c.client.Has("subscription") {
+		return c.client.Get("subscription").(subscription.Repository)
+	}
+
+	return c.general.Get("subscription").(subscription.Repository)
+}
+
+// GetTicketRepo will return the current ticket repository
+func (c *MultiContainer) GetTicketRepo() ticket.Repository {
+	if c.client.Has("ticket") {
+		return c.client.Get("ticket").(ticket.Repository)
+	}
+
+	return c.general.Get("ticket").(ticket.Repository)
+}
diff --git a/cmd/bm-config/cmd/apikey_create.go b/cmd/bm-config/cmd/apikey_create.go
index 915e957..cd4c7bf 100644
--- a/cmd/bm-config/cmd/apikey_create.go
+++ b/cmd/bm-config/cmd/apikey_create.go
@@ -24,10 +24,10 @@ import (
 	"os"
 	"time"
 
+	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
 	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/key"
-	"github.com/bitmaelum/bitmaelum-suite/internal/parse"
 	"github.com/spf13/cobra"
 )
 
@@ -56,7 +56,7 @@ Note: Creating an admin key can only be done locally on the mail-server.
 		}
 
 		// Our custom parser allows (and defaults) to using days
-		validDuration, err := parse.ValidDuration(*mgValid)
+		validDuration, err := internal.ValidDuration(*mgValid)
 		if err != nil {
 			fmt.Printf("Error: incorrect duration specified.\n")
 			os.Exit(1)
@@ -67,7 +67,7 @@ Note: Creating an admin key can only be done locally on the mail-server.
 			expires = time.Now().Add(validDuration)
 		}
 
-		err = parse.ManagementPermissions(*mgPerms)
+		err = internal.CheckManagementPermissions(*mgPerms)
 		if err != nil {
 			fmt.Printf("Error: %s\n", err)
 			os.Exit(1)
@@ -91,7 +91,7 @@ Note: Creating an admin key can only be done locally on the mail-server.
 		}
 
 		// Store API key into persistent storage
-		repo := container.GetAPIKeyRepo()
+		repo := container.Instance.GetAPIKeyRepo()
 		err = repo.Store(k)
 		if err != nil {
 			fmt.Printf("Error: cannot store key: %s\n", err)
diff --git a/cmd/bm-config/cmd/invite.go b/cmd/bm-config/cmd/invite.go
index b2e2d6a..e572878 100644
--- a/cmd/bm-config/cmd/invite.go
+++ b/cmd/bm-config/cmd/invite.go
@@ -24,9 +24,9 @@ import (
 	"fmt"
 	"time"
 
+	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/invite"
-	"github.com/bitmaelum/bitmaelum-suite/internal/parse"
+	"github.com/bitmaelum/bitmaelum-suite/internal/signature"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
 	"github.com/spf13/cobra"
 )
@@ -50,14 +50,14 @@ server. Only the specified address can register the account`,
 			return
 		}
 
-		duration, err := parse.ValidDuration(d)
+		duration, err := internal.ValidDuration(d)
 		if err != nil {
 			outError("incorrect duration specified", asJSON)
 			return
 		}
 
 		validUntil := time.Now().Add(duration)
-		token, err := invite.NewInviteToken(addr.Hash(), config.Routing.RoutingID, validUntil, config.Routing.PrivateKey)
+		token, err := signature.NewInviteToken(addr.Hash(), config.Routing.RoutingID, validUntil, config.Routing.PrivateKey)
 		if err != nil {
 			msg := fmt.Sprintf("error while inviting address: %s", err)
 			outError(msg, asJSON)
diff --git a/cmd/bm-mail/gui/layout/account.go b/cmd/bm-mail/gui/layout/account.go
index c74c21a..33a8c36 100644
--- a/cmd/bm-mail/gui/layout/account.go
+++ b/cmd/bm-mail/gui/layout/account.go
@@ -32,7 +32,7 @@ func NewAccountScreen(app app.Type) tview.Primitive {
 	list := tview.NewList().ShowSecondaryText(false)
 	list.SetBorder(true).SetTitle("Accounts")
 	for _, acc := range app.Vault.Store.Accounts {
-		list.AddItem(acc.Name+" <"+acc.Address+">", "", rune(0), nil)
+		list.AddItem(acc.Name+" <"+acc.Address.String()+">", "", rune(0), nil)
 	}
 
 	list2 := tview.NewList().ShowSecondaryText(false)
diff --git a/cmd/bm-mail/gui/layout/message-overview.go b/cmd/bm-mail/gui/layout/message-overview.go
index 4415f27..6e40099 100644
--- a/cmd/bm-mail/gui/layout/message-overview.go
+++ b/cmd/bm-mail/gui/layout/message-overview.go
@@ -38,7 +38,7 @@ func NewMessageOverviewScreen(app app.Type) tview.Primitive {
 
 	root := tview.NewTreeNode("Accounts").SetColor(tcell.ColorGreen).SetExpanded(true).SetSelectable(false)
 	for _, acc := range app.Vault.Store.Accounts {
-		accNode := tview.NewTreeNode(acc.Address).SetColor(tcell.ColorYellow)
+		accNode := tview.NewTreeNode(acc.Address.String()).SetColor(tcell.ColorYellow)
 		root.AddChild(accNode)
 	}
 
diff --git a/cmd/bm-send/README.md b/cmd/bm-send/README.md
new file mode 100644
index 0000000..47bf627
--- /dev/null
+++ b/cmd/bm-send/README.md
@@ -0,0 +1,11 @@
+# bm-send
+
+A simple tool that will let you send messages on behalf of a bitmaelum address.
+
+  $ ./bm-send --priv "PRIVKEY" --subject "a mail" --from "from!" --to "to!" --body "...." --attachment foo.zip
+  
+will use the following environment variables:
+
+    BITMAELUM_SEND_PRIVKEY
+    BITMAELUM_SEND_FROM
+
diff --git a/cmd/bm-send/main.go b/cmd/bm-send/main.go
new file mode 100644
index 0000000..4bd8f4e
--- /dev/null
+++ b/cmd/bm-send/main.go
@@ -0,0 +1,154 @@
+// Copyright (c) 2020 BitMaelum Authors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+package main
+
+import (
+	"fmt"
+	"math/rand"
+	"os"
+	"time"
+
+	"github.com/bitmaelum/bitmaelum-suite/internal/api"
+	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/internal/message"
+	"github.com/bitmaelum/bitmaelum-suite/internal/messages"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
+	"github.com/davecgh/go-spew/spew"
+	"github.com/jessevdk/go-flags"
+)
+
+type options struct {
+	PrivateKey  string   `short:"p" long:"private_key" description:"Private key" required:"true" env:"BITMAELUM_SEND_PRIVATE_KEY"`
+	Subject     string   `short:"s" long:"subject" description:"Subject" required:"true"`
+	From        string   `short:"f" long:"from" description:"Sender" required:"true" env:"BITMAELUM_SEND_FROM"`
+	To          string   `short:"t" long:"to" description:"Recipient" required:"true"`
+	Message     string   `short:"m" long:"message" description:"Default message"`
+	Blocks      []string `short:"b" long:"block" description:"Body block"`
+	Attachments []string `short:"a" long:"attachment" description:"Attachment"`
+}
+
+var opts options
+
+var (
+	fromAddr *address.Address
+	toAddr   *address.Address
+	privKey  *bmcrypto.PrivKey
+)
+
+func main() {
+	rand.Seed(time.Now().UnixNano())
+
+	parseFlags()
+	spew.Dump(opts)
+
+	// Set default message block
+	if opts.Message != "" {
+		opts.Blocks = append(opts.Blocks, "default:"+opts.Message)
+	}
+
+	// Fetch both sender and recipient info
+	svc := container.Instance.GetResolveService()
+	senderInfo, err := svc.ResolveAddress(fromAddr.Hash())
+	if err != nil {
+		fmt.Println("cannot resolve sender address")
+		os.Exit(1)
+	}
+	recipientInfo, err := svc.ResolveAddress(toAddr.Hash())
+	if err != nil {
+		fmt.Println("cannot resolve recipient address")
+		os.Exit(1)
+	}
+
+	// Setup addressing
+	addressing := message.NewAddressing(
+		*fromAddr,
+		privKey,
+		senderInfo.RoutingInfo.Routing,
+		*toAddr,
+		&recipientInfo.PublicKey,
+	)
+
+	// Compose mail
+	envelope, err := message.Compose(addressing, opts.Subject, opts.Blocks, opts.Attachments)
+	if err != nil {
+		fmt.Println("cannot compose message")
+		os.Exit(1)
+	}
+
+	// Send mail
+	client, err := api.NewAuthenticated(*fromAddr, privKey, senderInfo.RoutingInfo.Routing)
+	if err != nil {
+		fmt.Println("cannot connect to api")
+		os.Exit(1)
+	}
+
+	err = messages.Send(*client, envelope)
+	if err != nil {
+		fmt.Println("cannot send message: ", err)
+		os.Exit(1)
+	}
+}
+
+func parseFlags() {
+	parser := flags.NewParser(&opts, flags.IgnoreUnknown)
+	_, err := parser.Parse()
+
+	if err != nil {
+		flagsError, _ := err.(*flags.Error)
+		if flagsError.Type == flags.ErrHelp {
+			os.Exit(1)
+		}
+
+		parser.WriteHelp(os.Stderr)
+		os.Exit(1)
+	}
+
+	if opts.Message == "" && len(opts.Blocks) == 0 {
+		_, _ = fmt.Fprintln(os.Stderr, "Please specify either a message (-m) or one or more blocks (-b)")
+		os.Exit(1)
+	}
+
+	if opts.Message != "" && len(opts.Blocks) > 0 {
+		_, _ = fmt.Fprintln(os.Stderr, "Please specify either a message (-m) or one or more blocks (-b)")
+		os.Exit(1)
+	}
+
+	// Check key
+	privKey, err = bmcrypto.NewPrivKey(opts.PrivateKey)
+	if err != nil {
+		_, _ = fmt.Fprintln(os.Stderr, "Incorrect private key specified.")
+		os.Exit(1)
+	}
+
+	// Check from address
+	fromAddr, err = address.NewAddress(opts.From)
+	if err != nil {
+		_, _ = fmt.Fprintln(os.Stderr, "Incorrect sender address specified.")
+		os.Exit(1)
+	}
+
+	// Check to address
+	toAddr, err = address.NewAddress(opts.To)
+	if err != nil {
+		_, _ = fmt.Fprintln(os.Stderr, "Incorrect recipient address specified.")
+		os.Exit(1)
+	}
+}
diff --git a/cmd/bm-server/handler/account.go b/cmd/bm-server/handler/account.go
index 3121fca..e735122 100644
--- a/cmd/bm-server/handler/account.go
+++ b/cmd/bm-server/handler/account.go
@@ -24,9 +24,9 @@ import (
 	"fmt"
 	"net/http"
 
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
-	"github.com/bitmaelum/bitmaelum-suite/internal/invite"
+	"github.com/bitmaelum/bitmaelum-suite/internal/signature"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	pow "github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
@@ -82,7 +82,7 @@ func CreateAccount(w http.ResponseWriter, req *http.Request) {
 	// Check if we need to verify against the mail server key, or the organisation key
 	var pubKey = config.Routing.PublicKey
 	if !orgHash.IsEmpty() {
-		r := container.GetResolveService()
+		r := container.Instance.GetResolveService()
 		oh, err := hash.NewFromHash(input.OrgHash)
 		if err != nil {
 			ErrorOut(w, http.StatusBadRequest, "incorrect org hash")
@@ -99,14 +99,14 @@ func CreateAccount(w http.ResponseWriter, req *http.Request) {
 	}
 
 	// Verify token
-	it, err := invite.ParseInviteToken(input.Token)
+	it, err := signature.ParseInviteToken(input.Token)
 	if err != nil || !it.Verify(config.Routing.RoutingID, pubKey) {
 		ErrorOut(w, http.StatusBadRequest, "cannot validate token")
 		return
 	}
 
 	// Check if account exists
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	if ar.Exists(input.Addr) {
 		ErrorOut(w, http.StatusBadRequest, "account already exists")
 		return
@@ -134,7 +134,7 @@ func RetrieveOrganisation(w http.ResponseWriter, req *http.Request) {
 	}
 
 	// Check if account exists
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	if !ar.Exists(*haddr) {
 		ErrorOut(w, http.StatusNotFound, "address not found")
 		return
@@ -152,7 +152,7 @@ func RetrieveOrganisation(w http.ResponseWriter, req *http.Request) {
 	_ = json.NewEncoder(w).Encode(settings)
 }
 
-// RetrieveKeys is the handler that will retrieve public keys directly from the mailserver
+// RetrieveKeys is the handler that will retrieve public keys directly from the mail server
 func RetrieveKeys(w http.ResponseWriter, req *http.Request) {
 	haddr, err := hash.NewFromHash(mux.Vars(req)["addr"])
 	if err != nil {
@@ -161,7 +161,7 @@ func RetrieveKeys(w http.ResponseWriter, req *http.Request) {
 	}
 
 	// Check if account exists
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	if !ar.Exists(*haddr) {
 		ErrorOut(w, http.StatusNotFound, "public keys not found")
 		return
diff --git a/cmd/bm-server/handler/api_key.go b/cmd/bm-server/handler/api_key.go
index 47fc9dd..6ccf1d1 100644
--- a/cmd/bm-server/handler/api_key.go
+++ b/cmd/bm-server/handler/api_key.go
@@ -25,9 +25,9 @@ import (
 	"net/http"
 	"time"
 
+	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/key"
-	"github.com/bitmaelum/bitmaelum-suite/internal/parse"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/gorilla/mux"
 )
@@ -48,7 +48,7 @@ func CreateAPIKey(w http.ResponseWriter, req *http.Request) {
 	}
 
 	//
-	err = parse.AccountPermissions(input.Permissions)
+	err = internal.CheckAccountPermissions(input.Permissions)
 	if err != nil {
 		ErrorOut(w, http.StatusBadRequest, "incorrect permissions")
 		return
@@ -63,7 +63,7 @@ func CreateAPIKey(w http.ResponseWriter, req *http.Request) {
 	newAPIKey := key.NewAPIAccountKey(*h, input.Permissions, time.Unix(input.Expires, 0), input.Desc)
 
 	// Store API key into persistent storage
-	repo := container.GetAPIKeyRepo()
+	repo := container.Instance.GetAPIKeyRepo()
 	err = repo.Store(newAPIKey)
 	if err != nil {
 		msg := fmt.Sprintf("error while storing key: %s", err)
@@ -88,7 +88,7 @@ func ListAPIKeys(w http.ResponseWriter, req *http.Request) {
 	}
 
 	// Store API key into persistent storage
-	repo := container.GetAPIKeyRepo()
+	repo := container.Instance.GetAPIKeyRepo()
 	keys, err := repo.FetchByHash(h.String())
 	if err != nil {
 		msg := fmt.Sprintf("error while retrieving keys: %s", err)
@@ -113,7 +113,7 @@ func DeleteAPIKey(w http.ResponseWriter, req *http.Request) {
 	keyID := mux.Vars(req)["key"]
 
 	// Fetch key
-	repo := container.GetAPIKeyRepo()
+	repo := container.Instance.GetAPIKeyRepo()
 	k, err := repo.Fetch(keyID)
 	if err != nil || k.AddressHash.String() != h.String() {
 		// Only allow deleting of keys that we own as account
@@ -139,7 +139,7 @@ func GetAPIKeyDetails(w http.ResponseWriter, req *http.Request) {
 	keyID := mux.Vars(req)["key"]
 
 	// Fetch key
-	repo := container.GetAPIKeyRepo()
+	repo := container.Instance.GetAPIKeyRepo()
 	k, err := repo.Fetch(keyID)
 	if err != nil || k.AddressHash.String() != h.String() {
 		ErrorOut(w, http.StatusNotFound, "key not found")
diff --git a/cmd/bm-server/handler/auth_key.go b/cmd/bm-server/handler/auth_key.go
index 4ece148..f6ecc51 100644
--- a/cmd/bm-server/handler/auth_key.go
+++ b/cmd/bm-server/handler/auth_key.go
@@ -59,7 +59,7 @@ func CreateAuthKey(w http.ResponseWriter, req *http.Request) {
 	newAuthKey := key.NewAuthKey(*h, input.PublicKey, input.Signature, time.Unix(input.Expires, 0), input.Desc)
 
 	// Store auth key into persistent storage
-	repo := container.GetAuthKeyRepo()
+	repo := container.Instance.GetAuthKeyRepo()
 	err = repo.Store(newAuthKey)
 	if err != nil {
 		msg := fmt.Sprintf("error while storing key: %s", err)
@@ -84,7 +84,7 @@ func ListAuthKeys(w http.ResponseWriter, req *http.Request) {
 	}
 
 	// Store Auth key into persistent storage
-	repo := container.GetAuthKeyRepo()
+	repo := container.Instance.GetAuthKeyRepo()
 	keys, err := repo.FetchByHash(h.String())
 	if err != nil {
 		msg := fmt.Sprintf("error while retrieving keys: %s", err)
@@ -109,7 +109,7 @@ func DeleteAuthKey(w http.ResponseWriter, req *http.Request) {
 	keyID := mux.Vars(req)["key"]
 
 	// Fetch key
-	repo := container.GetAuthKeyRepo()
+	repo := container.Instance.GetAuthKeyRepo()
 	k, err := repo.Fetch(keyID)
 	if err != nil || k.AddressHash.String() != h.String() {
 		// Only allow deleting of keys that we own as account
@@ -135,7 +135,7 @@ func GetAuthKeyDetails(w http.ResponseWriter, req *http.Request) {
 	keyID := mux.Vars(req)["key"]
 
 	// Fetch key
-	repo := container.GetAuthKeyRepo()
+	repo := container.Instance.GetAuthKeyRepo()
 	k, err := repo.Fetch(keyID)
 	if err != nil || k.AddressHash.String() != h.String() {
 		ErrorOut(w, http.StatusNotFound, "key not found")
diff --git a/cmd/bm-server/handler/boxes.go b/cmd/bm-server/handler/boxes.go
index 3bd4b17..84a70ab 100644
--- a/cmd/bm-server/handler/boxes.go
+++ b/cmd/bm-server/handler/boxes.go
@@ -24,8 +24,8 @@ import (
 	"strconv"
 	"time"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/account"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/account"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/gorilla/mux"
 )
@@ -55,7 +55,7 @@ func CreateBox(w http.ResponseWriter, req *http.Request) {
 		return
 	}
 
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	err = ar.CreateBox(*haddr, input.ParentBoxID)
 	if err != nil {
 		ErrorOut(w, http.StatusBadRequest, err.Error())
@@ -80,7 +80,7 @@ func DeleteBox(w http.ResponseWriter, req *http.Request) {
 		return
 	}
 
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	err = ar.DeleteBox(*haddr, box)
 	if err != nil {
 		ErrorOut(w, http.StatusNotFound, err.Error())
@@ -100,7 +100,7 @@ func RetrieveBoxes(w http.ResponseWriter, req *http.Request) {
 	}
 
 	// Retrieve all boxes
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	boxes, err := ar.GetAllBoxes(*haddr)
 	if err != nil {
 		ErrorOut(w, http.StatusInternalServerError, "cannot read boxes")
@@ -161,7 +161,7 @@ func getMessageList(req *http.Request) (*account.MessageList, *httpError) {
 		}
 	}
 
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	if !ar.ExistsBox(*haddr, box) {
 		return nil, &httpError{
 			err:        accountNotFound,
diff --git a/cmd/bm-server/handler/incoming.go b/cmd/bm-server/handler/incoming.go
index fbb79d2..928c361 100644
--- a/cmd/bm-server/handler/incoming.go
+++ b/cmd/bm-server/handler/incoming.go
@@ -28,7 +28,6 @@ import (
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/processor"
 	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/message"
-	"github.com/bitmaelum/bitmaelum-suite/internal/server"
 	"github.com/gorilla/mux"
 )
 
@@ -63,7 +62,7 @@ func IncomingMessageHeader(w http.ResponseWriter, req *http.Request) {
 	}
 
 	// Add a server signature to the header, so we know this is the origin of the message
-	err = server.SignHeader(header)
+	err = message.SignServerHeader(header)
 	if err != nil {
 		ErrorOut(w, http.StatusInternalServerError, "error while signing incoming message")
 		return
@@ -160,7 +159,7 @@ func CompleteIncoming(w http.ResponseWriter, req *http.Request) {
 	processor.QueueIncomingMessage(t.ID)
 
 	// Remove ticket
-	ticketRepo := container.GetTicketRepo()
+	ticketRepo := container.Instance.GetTicketRepo()
 	ticketRepo.Remove(t.ID)
 
 	w.Header().Set("Content-Type", "application/json")
@@ -189,7 +188,7 @@ func DeleteIncoming(w http.ResponseWriter, req *http.Request) {
 	}
 
 	// Remove ticket
-	ticketRepo := container.GetTicketRepo()
+	ticketRepo := container.Instance.GetTicketRepo()
 	ticketRepo.Remove(t.ID)
 
 	w.Header().Set("Content-Type", "application/json")
diff --git a/cmd/bm-server/handler/message.go b/cmd/bm-server/handler/message.go
index 551cce8..5b0fa25 100644
--- a/cmd/bm-server/handler/message.go
+++ b/cmd/bm-server/handler/message.go
@@ -24,8 +24,8 @@ import (
 	"net/http"
 	"strconv"
 
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/gorilla/mux"
 	"github.com/sirupsen/logrus"
@@ -51,7 +51,7 @@ func GetMessage(w http.ResponseWriter, req *http.Request) {
 
 	messageID := mux.Vars(req)["message"]
 
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	header, _ := ar.FetchMessageHeader(*haddr, box, messageID)
 	catalog, _ := ar.FetchMessageCatalog(*haddr, box, messageID)
 
@@ -81,7 +81,7 @@ func GetMessageBlock(w http.ResponseWriter, req *http.Request) {
 	messageID := mux.Vars(req)["message"]
 	blockID := mux.Vars(req)["block"]
 
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	block, err := ar.FetchMessageBlock(*haddr, box, messageID, blockID)
 	if err != nil {
 		ErrorOut(w, http.StatusBadRequest, incorrectBox)
@@ -110,7 +110,7 @@ func GetMessageAttachment(w http.ResponseWriter, req *http.Request) {
 	messageID := mux.Vars(req)["message"]
 	attachmentID := mux.Vars(req)["attachment"]
 
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	attachment, size, err := ar.FetchMessageAttachment(*haddr, box, messageID, attachmentID)
 	if err != nil {
 		ErrorOut(w, http.StatusBadRequest, "incorrect attachment")
diff --git a/cmd/bm-server/handler/mgmt/api_key.go b/cmd/bm-server/handler/mgmt/api_key.go
index 168b1b9..bad9be7 100644
--- a/cmd/bm-server/handler/mgmt/api_key.go
+++ b/cmd/bm-server/handler/mgmt/api_key.go
@@ -26,9 +26,9 @@ import (
 	"time"
 
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/handler"
+	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/key"
-	"github.com/bitmaelum/bitmaelum-suite/internal/parse"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 )
 
@@ -60,12 +60,12 @@ func NewAPIKey(w http.ResponseWriter, req *http.Request) {
 	}
 
 	k := handler.GetAPIKey(req)
-	if !k.HasPermission(key.PermAPIKeys, h) {
+	if !k.HasPermission(internal.PermAPIKeys, h) {
 		handler.ErrorOut(w, http.StatusUnauthorized, "unauthorized")
 		return
 	}
 
-	err = parse.ManagementPermissions(input.Permissions)
+	err = internal.CheckManagementPermissions(input.Permissions)
 	if err != nil {
 		handler.ErrorOut(w, http.StatusBadRequest, "incorrect permissions")
 		return
@@ -74,7 +74,7 @@ func NewAPIKey(w http.ResponseWriter, req *http.Request) {
 	newAPIKey := key.NewAPIAccountKey(*h, input.Permissions, time.Unix(input.Expires, 0), input.Desc)
 
 	// Store API key into persistent storage
-	repo := container.GetAPIKeyRepo()
+	repo := container.Instance.GetAPIKeyRepo()
 	err = repo.Store(newAPIKey)
 	if err != nil {
 		msg := fmt.Sprintf("error while storing key: %s", err)
diff --git a/cmd/bm-server/handler/mgmt/flush.go b/cmd/bm-server/handler/mgmt/flush.go
index a80c52e..ea14cbf 100644
--- a/cmd/bm-server/handler/mgmt/flush.go
+++ b/cmd/bm-server/handler/mgmt/flush.go
@@ -26,13 +26,12 @@ import (
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/handler"
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/processor"
 	"github.com/bitmaelum/bitmaelum-suite/internal"
-	"github.com/bitmaelum/bitmaelum-suite/internal/key"
 )
 
 // FlushQueues handler will flush all the queues normally on tickers
 func FlushQueues(w http.ResponseWriter, req *http.Request) {
 	k := handler.GetAPIKey(req)
-	if !k.HasPermission(key.PermFlush, nil) {
+	if !k.HasPermission(internal.PermFlush, nil) {
 		handler.ErrorOut(w, http.StatusUnauthorized, "unauthorized")
 		return
 	}
diff --git a/cmd/bm-server/handler/mgmt/invite.go b/cmd/bm-server/handler/mgmt/invite.go
index 7639606..6c2c2d5 100644
--- a/cmd/bm-server/handler/mgmt/invite.go
+++ b/cmd/bm-server/handler/mgmt/invite.go
@@ -25,9 +25,9 @@ import (
 	"time"
 
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/handler"
+	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/invite"
-	"github.com/bitmaelum/bitmaelum-suite/internal/key"
+	"github.com/bitmaelum/bitmaelum-suite/internal/signature"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 )
 
@@ -41,7 +41,7 @@ type jsonOut map[string]interface{}
 // NewInvite handler will generate a new invite token for a given address
 func NewInvite(w http.ResponseWriter, req *http.Request) {
 	k := handler.GetAPIKey(req)
-	if !k.HasPermission(key.PermGenerateInvites, nil) {
+	if !k.HasPermission(internal.PermGenerateInvites, nil) {
 		handler.ErrorOut(w, http.StatusUnauthorized, "unauthorized")
 		return
 	}
@@ -60,7 +60,7 @@ func NewInvite(w http.ResponseWriter, req *http.Request) {
 	}
 
 	validUntil := time.Now().Add(time.Duration(input.Days) * 24 * time.Hour)
-	token, err := invite.NewInviteToken(*addrHash, config.Routing.RoutingID, validUntil, config.Routing.PrivateKey)
+	token, err := signature.NewInviteToken(*addrHash, config.Routing.RoutingID, validUntil, config.Routing.PrivateKey)
 	if err != nil {
 		handler.ErrorOut(w, http.StatusInternalServerError, "cannot generate invite token")
 		return
diff --git a/cmd/bm-server/handler/ticket.go b/cmd/bm-server/handler/ticket.go
index e40b9e3..ba1715b 100644
--- a/cmd/bm-server/handler/ticket.go
+++ b/cmd/bm-server/handler/ticket.go
@@ -24,7 +24,7 @@ import (
 	"errors"
 	"net/http"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/subscription"
 	"github.com/bitmaelum/bitmaelum-suite/internal/ticket"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
@@ -85,7 +85,7 @@ func GetClientToServerTicket(w http.ResponseWriter, req *http.Request) {
 	// @TODO: subscription ID is empty here, but we probably want to fetch this directly from the server, not from the
 	//  ticket body request (clients do not know anything about subscription ids)
 	t := ticket.NewValidated(requestInfo.From, requestInfo.To, requestInfo.SubscriptionID)
-	ticketRepo := container.GetTicketRepo()
+	ticketRepo := container.Instance.GetTicketRepo()
 	err = ticketRepo.Store(t)
 	if err != nil {
 		logrus.Trace("cannot save ticket: ", err)
@@ -143,7 +143,7 @@ func GetServerToServerTicket(w http.ResponseWriter, req *http.Request) {
 		tckt := ticket.NewUnvalidated(requestInfo.From, requestInfo.To, requestInfo.SubscriptionID)
 
 		// Store ticket
-		ticketRepo := container.GetTicketRepo()
+		ticketRepo := container.Instance.GetTicketRepo()
 		err = ticketRepo.Store(tckt)
 		if err != nil {
 			ErrorOut(w, http.StatusInternalServerError, cantSaveTicket)
@@ -156,7 +156,7 @@ func GetServerToServerTicket(w http.ResponseWriter, req *http.Request) {
 	}
 
 	// Find the ticket in the repo
-	ticketRepo := container.GetTicketRepo()
+	ticketRepo := container.Instance.GetTicketRepo()
 	tckt, err := ticketRepo.Fetch(requestInfo.TicketID)
 	if err != nil {
 		ErrorOut(w, http.StatusPreconditionFailed, "ticket not found")
@@ -175,7 +175,7 @@ func GetServerToServerTicket(w http.ResponseWriter, req *http.Request) {
 	tckt.Proof.Proof = requestInfo.Proof
 	tckt.Valid = tckt.Proof.HasDoneWork() && tckt.Proof.IsValid()
 	if tckt.Valid {
-		ticketRepo := container.GetTicketRepo()
+		ticketRepo := container.Instance.GetTicketRepo()
 		err = ticketRepo.Store(tckt)
 		if err != nil {
 			ErrorOut(w, http.StatusInternalServerError, cantSaveTicket)
@@ -199,7 +199,7 @@ func outputTicket(tckt *ticket.Ticket, w http.ResponseWriter) {
 }
 
 func handleSubscription(requestInfo *requestInfoType) (*ticket.Ticket, error) {
-	subscriptionRepo := container.GetSubscriptionRepo()
+	subscriptionRepo := container.Instance.GetSubscriptionRepo()
 
 	// Check if we have the subscription stored
 	sub := subscription.New(requestInfo.From, requestInfo.To, requestInfo.SubscriptionID)
@@ -214,7 +214,7 @@ func handleSubscription(requestInfo *requestInfoType) (*ticket.Ticket, error) {
 	tckt := ticket.NewValidated(requestInfo.From, requestInfo.To, requestInfo.SubscriptionID)
 
 	// Store the new validated ticket back in the repo
-	ticketRepo := container.GetTicketRepo()
+	ticketRepo := container.Instance.GetTicketRepo()
 	err := ticketRepo.Store(tckt)
 	if err != nil {
 		return nil, &httpError{
@@ -229,7 +229,7 @@ func handleSubscription(requestInfo *requestInfoType) (*ticket.Ticket, error) {
 
 // validateLocalAddress checks if the recipient in the ticket is a local address. Returns error if not
 func validateLocalAddress(addr hash.Hash) error {
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	if ar.Exists(addr) {
 		return nil
 	}
@@ -244,7 +244,7 @@ func validateLocalAddress(addr hash.Hash) error {
 func fetchTicketHeader(req *http.Request) (*ticket.Ticket, error) {
 	ticketID := req.Header.Get(ticket.TicketHeader)
 
-	ticketRepo := container.GetTicketRepo()
+	ticketRepo := container.Instance.GetTicketRepo()
 	t, err := ticketRepo.Fetch(ticketID)
 	if err != nil {
 		return nil, err
diff --git a/internal/account/box.go b/cmd/bm-server/internal/account/box.go
similarity index 100%
rename from internal/account/box.go
rename to cmd/bm-server/internal/account/box.go
diff --git a/internal/account/file.go b/cmd/bm-server/internal/account/file.go
similarity index 100%
rename from internal/account/file.go
rename to cmd/bm-server/internal/account/file.go
diff --git a/internal/account/file_account.go b/cmd/bm-server/internal/account/file_account.go
similarity index 100%
rename from internal/account/file_account.go
rename to cmd/bm-server/internal/account/file_account.go
diff --git a/internal/account/file_box.go b/cmd/bm-server/internal/account/file_box.go
similarity index 100%
rename from internal/account/file_box.go
rename to cmd/bm-server/internal/account/file_box.go
diff --git a/internal/account/file_key.go b/cmd/bm-server/internal/account/file_key.go
similarity index 100%
rename from internal/account/file_key.go
rename to cmd/bm-server/internal/account/file_key.go
diff --git a/internal/account/file_message.go b/cmd/bm-server/internal/account/file_message.go
similarity index 100%
rename from internal/account/file_message.go
rename to cmd/bm-server/internal/account/file_message.go
diff --git a/internal/account/file_organisation.go b/cmd/bm-server/internal/account/file_organisation.go
similarity index 100%
rename from internal/account/file_organisation.go
rename to cmd/bm-server/internal/account/file_organisation.go
diff --git a/internal/account/mock.go b/cmd/bm-server/internal/account/mock.go
similarity index 100%
rename from internal/account/mock.go
rename to cmd/bm-server/internal/account/mock.go
diff --git a/internal/account/repository.go b/cmd/bm-server/internal/account/repository.go
similarity index 100%
rename from internal/account/repository.go
rename to cmd/bm-server/internal/account/repository.go
diff --git a/internal/container/account.go b/cmd/bm-server/internal/container/account.go
similarity index 92%
rename from internal/container/account.go
rename to cmd/bm-server/internal/container/account.go
index 7358b10..65c8e01 100644
--- a/internal/container/account.go
+++ b/cmd/bm-server/internal/container/account.go
@@ -22,7 +22,7 @@ package container
 import (
 	"sync"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/account"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/account"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
 )
 
@@ -40,5 +40,5 @@ func setupAccountRepo() (interface{}, error) {
 }
 
 func init() {
-	Set("account", setupAccountRepo)
+	Instance.SetShared("account", setupAccountRepo)
 }
diff --git a/cmd/bm-server/internal/container/container.go b/cmd/bm-server/internal/container/container.go
new file mode 100644
index 0000000..20d9025
--- /dev/null
+++ b/cmd/bm-server/internal/container/container.go
@@ -0,0 +1,117 @@
+// Copyright (c) 2020 BitMaelum Authors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+package container
+
+import (
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/account"
+	maincontainer "github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/internal/key"
+	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
+	"github.com/bitmaelum/bitmaelum-suite/internal/subscription"
+	"github.com/bitmaelum/bitmaelum-suite/internal/ticket"
+)
+
+/*
+ * MultiContainer is a container struct that acts like a switch between the "client" container, and the "general" container.
+ * The reason this is not a single container, is that the client container has additional methods which can be called, for
+ * instance: GetAccountRepo().
+ *
+ * So, all the code in the bm-server directory will use this container, so it can fetch the Account Repo when needed, but
+ * also can fetch other repositories from the same instance. From the code point of view, all is located in the same container.
+ *
+ * I really think there are better ways to deal with this, but this is the best i can do for now.
+ */
+
+// MultiContainer is a struct that holds both the general and client container
+type MultiContainer struct {
+	general maincontainer.Container // The general container used in the internal packages
+	client  maincontainer.Container // The client container used in this command
+}
+
+// Instance is the main bitmaelum service container
+var Instance = MultiContainer{
+	general: &maincontainer.Instance,
+	client:  maincontainer.NewContainer(),
+}
+
+// SetShared will set a definition inside the client container
+func (c *MultiContainer) SetShared(key string, f maincontainer.ServiceFunc) {
+	c.client.SetShared(key, f)
+}
+
+// SetNonShared will set a definition inside the client container
+func (c *MultiContainer) SetNonShared(key string, f maincontainer.ServiceFunc) {
+	c.client.SetNonShared(key, f)
+}
+
+// Get will fetch a definition from the client container
+func (c *MultiContainer) Get(key string) interface{} {
+	return c.client.Get(key)
+}
+
+// GetAccountRepo will return the current account repository
+func (c *MultiContainer) GetAccountRepo() account.Repository {
+	return c.client.Get("account").(account.Repository)
+}
+
+// GetAPIKeyRepo will return the current api key repository
+func (c *MultiContainer) GetAPIKeyRepo() key.APIKeyRepo {
+	if c.client.Has("api-key") {
+		return c.client.Get("api-key").(key.APIKeyRepo)
+	}
+
+	return c.general.Get("api-key").(key.APIKeyRepo)
+}
+
+// GetAuthKeyRepo will return the current auth key repository
+func (c *MultiContainer) GetAuthKeyRepo() key.AuthKeyRepo {
+	if c.client.Has("auth-key") {
+		return c.client.Get("auth-key").(key.AuthKeyRepo)
+	}
+
+	return c.general.Get("auth-key").(key.AuthKeyRepo)
+}
+
+// GetResolveService will return the current resolver service
+func (c *MultiContainer) GetResolveService() *resolver.Service {
+	if c.client.Has("resolver") {
+		return c.client.Get("resolver").(*resolver.Service)
+	}
+
+	return c.general.Get("resolver").(*resolver.Service)
+}
+
+// GetSubscriptionRepo will return the current subscription repository
+func (c *MultiContainer) GetSubscriptionRepo() subscription.Repository {
+	if c.client.Has("subscription") {
+		return c.client.Get("subscription").(subscription.Repository)
+	}
+
+	return c.general.Get("subscription").(subscription.Repository)
+}
+
+// GetTicketRepo will return the current ticket repository
+func (c *MultiContainer) GetTicketRepo() ticket.Repository {
+	if c.client.Has("ticket") {
+		return c.client.Get("ticket").(ticket.Repository)
+	}
+
+	return c.general.Get("ticket").(ticket.Repository)
+}
diff --git a/internal/container/proof-of-work.go b/cmd/bm-server/internal/container/proof-of-work.go
similarity index 87%
rename from internal/container/proof-of-work.go
rename to cmd/bm-server/internal/container/proof-of-work.go
index 6e4b846..20c36c8 100644
--- a/internal/container/proof-of-work.go
+++ b/cmd/bm-server/internal/container/proof-of-work.go
@@ -23,14 +23,14 @@ import (
 	"os"
 	"sync"
 
-	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/storage"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/pow"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
 	"github.com/go-redis/redis/v8"
 )
 
 var (
 	proofOnce    sync.Once
-	proofService storage.Storable
+	proofService pow.Storable
 )
 
 func setupProofService() (interface{}, error) {
@@ -42,13 +42,13 @@ func setupProofService() (interface{}, error) {
 				DB:   config.Server.Redis.Db,
 			}
 
-			proofService = storage.NewRedis(&opts)
+			proofService = pow.NewRedis(&opts)
 		} else {
 			if config.Server.Bolt.DatabasePath == "" {
 				config.Server.Bolt.DatabasePath = os.TempDir()
 			}
 
-			proofService = storage.NewBolt(&config.Server.Bolt.DatabasePath)
+			proofService = pow.NewBolt(&config.Server.Bolt.DatabasePath)
 		}
 
 	})
@@ -57,5 +57,5 @@ func setupProofService() (interface{}, error) {
 }
 
 func init() {
-	Set("proof-of-work", setupProofService)
+	Instance.SetShared("proof-of-work", setupProofService)
 }
diff --git a/cmd/bm-server/storage/bolt.go b/cmd/bm-server/internal/pow/bolt.go
similarity index 95%
rename from cmd/bm-server/storage/bolt.go
rename to cmd/bm-server/internal/pow/bolt.go
index fcc2d58..9e6b1af 100644
--- a/cmd/bm-server/storage/bolt.go
+++ b/cmd/bm-server/internal/pow/bolt.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package storage
+package pow
 
 import (
 	"encoding/json"
@@ -33,10 +33,10 @@ type boltStorage struct {
 	client *bolt.DB
 }
 
-//BucketName is the bucket name to store the invitations on the bolt db
+// BucketName is the bucket name to store the invitations on the bolt db
 const BucketName = "pow"
 
-//BoltDBFile is the filename to store the boltdb database
+// BoltDBFile is the filename to store the boltdb database
 const BoltDBFile = "pow.db"
 
 // NewBolt initializes a new repository
diff --git a/cmd/bm-server/storage/redis.go b/cmd/bm-server/internal/pow/redis.go
similarity index 99%
rename from cmd/bm-server/storage/redis.go
rename to cmd/bm-server/internal/pow/redis.go
index 4b58d8e..4f0d387 100644
--- a/cmd/bm-server/storage/redis.go
+++ b/cmd/bm-server/internal/pow/redis.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package storage
+package pow
 
 import (
 	"encoding/json"
diff --git a/cmd/bm-server/storage/storage.go b/cmd/bm-server/internal/pow/storage.go
similarity index 99%
rename from cmd/bm-server/storage/storage.go
rename to cmd/bm-server/internal/pow/storage.go
index 9d74805..6520fa1 100644
--- a/cmd/bm-server/storage/storage.go
+++ b/cmd/bm-server/internal/pow/storage.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package storage
+package pow
 
 import (
 	"crypto/rand"
diff --git a/cmd/bm-server/main.go b/cmd/bm-server/main.go
index a1e1aee..999d088 100644
--- a/cmd/bm-server/main.go
+++ b/cmd/bm-server/main.go
@@ -31,13 +31,14 @@ import (
 
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/handler"
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/handler/mgmt"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/middleware"
-	auth "github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/middleware/auth"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/middleware/auth"
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/processor"
 	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/message"
+	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
 	"github.com/gorilla/handlers"
 	"github.com/gorilla/mux"
 	"github.com/sirupsen/logrus"
@@ -67,7 +68,7 @@ func main() {
 
 	logrus.Info("Starting " + internal.VersionString("bm-server"))
 
-	_ = container.GetResolveService()
+	// _ = container.Instance.GetResolveService()
 
 	// setup context so we can easily stop all components of the server
 	ctx, cancel := context.WithCancel(context.Background())
@@ -106,16 +107,15 @@ You can generate a new one by running:
 	}
 
 	// Check if route exist on the key resolver, and upload new info if needed
-	res := container.GetResolveService()
+	res := container.Instance.GetResolveService()
 	info, err := res.ResolveRouting(config.Routing.RoutingID)
 	if err != nil || info.Routing != config.Server.Server.Hostname {
 		// Upload routing
-		err := res.UploadRoutingInfo(internal.RoutingInfo{
-			RoutingID: config.Routing.RoutingID,
-			PrivKey:   config.Routing.PrivateKey,
-			PubKey:    config.Routing.PublicKey,
-			Route:     config.Server.Server.Hostname,
-		})
+		err := res.UploadRoutingInfo(resolver.RoutingInfo{
+			Hash:      config.Routing.RoutingID,
+			PublicKey: config.Routing.PublicKey,
+			Routing:   config.Server.Server.Hostname,
+		}, &config.Routing.PrivateKey)
 		if err != nil {
 			fmt.Print("There is an error while uploading routing information to the key resolver: ", err)
 			os.Exit(1)
diff --git a/cmd/bm-server/middleware/auth/auth_apikey.go b/cmd/bm-server/middleware/auth/auth_apikey.go
index 725c4cc..af48af2 100644
--- a/cmd/bm-server/middleware/auth/auth_apikey.go
+++ b/cmd/bm-server/middleware/auth/auth_apikey.go
@@ -26,7 +26,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/key"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/gorilla/mux"
@@ -68,7 +68,7 @@ func (a *APIKeyAuth) Authenticate(req *http.Request, route string) (context.Cont
 		return nil, false
 	}
 
-	accountRepo := container.GetAccountRepo()
+	accountRepo := container.Instance.GetAccountRepo()
 	if !accountRepo.Exists(*haddr) {
 		logrus.Trace("auth: address not found")
 		return nil, false
@@ -132,7 +132,7 @@ func (*APIKeyAuth) getAPIKey(bearerToken string) (*key.APIKeyType, error) {
 	}
 	apiKeyID := bearerToken[7:]
 
-	apiKeyRepo := container.GetAPIKeyRepo()
+	apiKeyRepo := container.Instance.GetAPIKeyRepo()
 	k, err := apiKeyRepo.Fetch(apiKeyID)
 	if err != nil {
 		return nil, ErrInvalidAPIKey
diff --git a/cmd/bm-server/middleware/auth/auth_jwt.go b/cmd/bm-server/middleware/auth/auth_jwt.go
index 18b9270..ffdb6b6 100644
--- a/cmd/bm-server/middleware/auth/auth_jwt.go
+++ b/cmd/bm-server/middleware/auth/auth_jwt.go
@@ -25,8 +25,8 @@ import (
 	"net/http"
 	"strings"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/internal/api"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/gorilla/mux"
 	"github.com/sirupsen/logrus"
@@ -54,7 +54,7 @@ func (mw *JwtAuth) Authenticate(req *http.Request, _ string) (context.Context, b
 		return nil, false
 	}
 
-	accountRepo := container.GetAccountRepo()
+	accountRepo := container.Instance.GetAccountRepo()
 	if !accountRepo.Exists(*haddr) {
 		logrus.Trace("auth: address not found")
 		return nil, false
@@ -87,7 +87,7 @@ func checkToken(bearerToken string, addr hash.Hash) (*jwt.Token, error) {
 	}
 	tokenString := bearerToken[7:]
 
-	accountRepo := container.GetAccountRepo()
+	accountRepo := container.Instance.GetAccountRepo()
 	keys, err := accountRepo.FetchKeys(addr)
 	if err != nil {
 		logrus.Trace("auth: cannot fetch keys: ", err)
@@ -95,7 +95,7 @@ func checkToken(bearerToken string, addr hash.Hash) (*jwt.Token, error) {
 	}
 
 	for _, key := range keys {
-		token, err := internal.ValidateJWTToken(tokenString, addr, key)
+		token, err := api.ValidateJWTToken(tokenString, addr, key)
 		if err == nil {
 			return token, nil
 		}
diff --git a/cmd/bm-server/middleware/auth/auth_onbehalf_jwt.go b/cmd/bm-server/middleware/auth/auth_onbehalf_jwt.go
index cf643b1..09e6b9e 100644
--- a/cmd/bm-server/middleware/auth/auth_onbehalf_jwt.go
+++ b/cmd/bm-server/middleware/auth/auth_onbehalf_jwt.go
@@ -24,8 +24,8 @@ import (
 	"net/http"
 	"strings"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/internal/api"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/gorilla/mux"
 	"github.com/sirupsen/logrus"
@@ -43,7 +43,7 @@ func (mw *OnBehalfJwtAuth) Authenticate(req *http.Request, _ string) (context.Co
 		return nil, false
 	}
 
-	accountRepo := container.GetAccountRepo()
+	accountRepo := container.Instance.GetAccountRepo()
 	if !accountRepo.Exists(*haddr) {
 		logrus.Trace("auth: address not found")
 		return nil, false
@@ -76,7 +76,7 @@ func checkOnBehalfToken(bearerToken string, addr hash.Hash) (*jwt.Token, error)
 	}
 	tokenString := bearerToken[7:]
 
-	authRepo := container.GetAuthKeyRepo()
+	authRepo := container.Instance.GetAuthKeyRepo()
 	keys, err := authRepo.FetchByHash(addr.String())
 	if err != nil {
 		logrus.Trace("auth: cannot fetch keys: ", err)
@@ -84,7 +84,7 @@ func checkOnBehalfToken(bearerToken string, addr hash.Hash) (*jwt.Token, error)
 	}
 
 	for _, key := range keys {
-		token, err := internal.ValidateJWTToken(tokenString, addr, *key.PublicKey)
+		token, err := api.ValidateJWTToken(tokenString, addr, *key.PublicKey)
 		if err != nil {
 			continue
 		}
diff --git a/cmd/bm-server/processor/process.go b/cmd/bm-server/processor/process.go
index a8b8e51..4da80bc 100644
--- a/cmd/bm-server/processor/process.go
+++ b/cmd/bm-server/processor/process.go
@@ -24,14 +24,11 @@ import (
 	"io/ioutil"
 	"os"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/account"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/account"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/api"
-	"github.com/bitmaelum/bitmaelum-suite/internal/client"
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/message"
 	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
-	"github.com/bitmaelum/bitmaelum-suite/internal/server"
 	"github.com/bitmaelum/bitmaelum-suite/internal/ticket"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/sirupsen/logrus"
@@ -60,7 +57,7 @@ func ProcessMessage(msgID string) {
 		return
 	}
 
-	rs := container.GetResolveService()
+	rs := container.Instance.GetResolveService()
 	addrInfo, err := rs.ResolveAddress(header.To.Addr)
 	if err != nil {
 		logrus.Trace(err)
@@ -70,7 +67,7 @@ func ProcessMessage(msgID string) {
 	}
 
 	// Local addresses don't need to be send. They are treated locally
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	if ar.Exists(header.To.Addr) {
 		// Do stuff locally
 		logrus.Debugf("Message %s can be transferred locally to %s", msgID, addrInfo.Hash)
@@ -95,7 +92,7 @@ func ProcessMessage(msgID string) {
 // deliverLocal moves a message to a local mailbox.
 func deliverLocal(addrInfo *resolver.AddressInfo, msgID string, header *message.Header) error {
 	// Check the serverSignature
-	if !server.VerifyHeader(*header) {
+	if !message.VerifyServerHeader(*header) {
 		logrus.Errorf("message %s destined for %s has failed the server signature check. Seems that this message did not originate from the original mail server. Removing the message.", msgID, header.To.Addr)
 
 		err := message.RemoveMessage(message.SectionProcessing, msgID)
@@ -105,7 +102,7 @@ func deliverLocal(addrInfo *resolver.AddressInfo, msgID string, header *message.
 	}
 
 	// Check the clientSignature
-	if !client.VerifyHeader(*header) {
+	if !message.VerifyClientHeader(*header) {
 		logrus.Errorf("message %s destined for %s has failed the client signature check. Seems that this message may have been spoofed. Removing the message.", msgID, header.To.Addr)
 
 		err := message.RemoveMessage(message.SectionProcessing, msgID)
@@ -120,7 +117,7 @@ func deliverLocal(addrInfo *resolver.AddressInfo, msgID string, header *message.
 		return err
 	}
 
-	ar := container.GetAccountRepo()
+	ar := container.Instance.GetAccountRepo()
 	err = ar.SendToBox(*h, account.BoxInbox, msgID)
 	if err != nil {
 		// Something went wrong.. let's try and move the message back to the retry queue
@@ -136,7 +133,7 @@ func deliverLocal(addrInfo *resolver.AddressInfo, msgID string, header *message.
 // we get the ticket. Once we have the ticket, we can upload the message to the server in the same way
 // we upload a message from a client to a server.
 func deliverRemote(addrInfo *resolver.AddressInfo, msgID string, header *message.Header) error {
-	rs := container.GetResolveService()
+	rs := container.Instance.GetResolveService()
 	routingInfo, err := rs.ResolveRouting(addrInfo.RoutingID)
 	if err != nil {
 		logrus.Warnf("cannot find routing ID %s for %s. Retrying.", addrInfo.RoutingID, header.To.Addr)
@@ -265,9 +262,5 @@ func processTicket(routingInfo resolver.RoutingInfo, addrInfo resolver.AddressIn
 
 // getClient will return an API client pointing to the actual mail server found in the routing info
 func getClient(routingInfo resolver.RoutingInfo) (*api.API, error) {
-	return api.NewAnonymous(api.ClientOpts{
-		Host:          routingInfo.Routing,
-		AllowInsecure: config.Server.Server.AllowInsecure,
-		Debug:         config.Client.Server.DebugHTTP,
-	})
+	return api.NewAnonymous(routingInfo.Routing)
 }
diff --git a/cmd/bm-server/processor/stuck.go b/cmd/bm-server/processor/stuck.go
index 1748d94..9318806 100644
--- a/cmd/bm-server/processor/stuck.go
+++ b/cmd/bm-server/processor/stuck.go
@@ -40,7 +40,7 @@ func ProcessStuckIncomingMessages() {
 		return
 	}
 
-	ticketRepo := container.GetTicketRepo()
+	ticketRepo := container.Instance.GetTicketRepo()
 	for _, fileInfo := range files {
 		// Not a dir, so not a message
 		if !fileInfo.IsDir() {
diff --git a/docs/status.md b/docs/status.md
deleted file mode 100644
index 33b571a..0000000
--- a/docs/status.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# Current status
-
-This document describes the current status of the project. It's in its current form completely 
-useless and consists of small components that may or may not work as advertised. The current goal 
-is to make sure our happy-path works the way it should be: we should be able to send mail, and 
-receive it.
-
-To make this happen, we need lots of components: message structures, encryption methods, server 
-APIs, client connectivity etc. This is all still very much work in progress. 
-
-## We are currently able to
-
- - [X] Add and manage public keys on the main resolver service: resolver.bitmaelum.com
- - [X] Run the message server with minimum capabilities
- - [X] Generate accounts and save them to the server and main resolver through bm-client.
- - [X] List accounts through bm-client
- - [X] Compose email, and uploading to the mail-server through bm-client
- - [X] Read email through utility
- - [X] Setting status flags on the mail-server (@todo: do we want this on the server, and if so, unencrypted?)
- - [X] Send email that is uploaded to the mail-server to an actual destination mail-server (or itself)
- - [X] Read messages in your mailboxes from bm-client
- 
-## Next up in our todo
-
- - [ ] Deal with organisations
- 
-
-## Later
-
- - [ ] Deal with multiple recipients (multi-header uploads)
- - [ ] Mailing lists
- - [ ] Create a simple mail client UI (electronjs most likely)
-
-
-## Much later
-
- - Just about everything else...
diff --git a/go.mod b/go.mod
index 23cbc61..d687113 100644
--- a/go.mod
+++ b/go.mod
@@ -3,17 +3,21 @@ module github.com/bitmaelum/bitmaelum-suite
 go 1.13
 
 require (
+	dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363 // indirect
 	github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee
 	github.com/cloudflare/gokey v0.1.0
 	github.com/coreos/go-semver v0.3.0
 	github.com/davecgh/go-spew v1.1.1
 	github.com/ernesto-jimenez/httplogger v0.0.0-20150224132909-86cc44f6150a
+	github.com/fzipp/gocyclo v0.3.1 // indirect
 	github.com/gabriel-vasile/mimetype v1.1.1
 	github.com/gdamore/tcell v1.4.0
 	github.com/gen2brain/beeep v0.0.0-20200526185328-e9c15c258e28
 	github.com/go-redis/redis/v8 v8.2.2
+	github.com/google/addlicense v0.0.0-20200906110928-a0294312aa76 // indirect
 	github.com/google/uuid v1.1.1
 	github.com/gookit/color v1.2.7
+	github.com/gordonklaus/ineffassign v0.0.0-20201107091007-3b93a8888063 // indirect
 	github.com/gorilla/handlers v1.4.2
 	github.com/gorilla/mux v1.7.4
 	github.com/hashicorp/golang-lru v0.5.4
@@ -37,9 +41,9 @@ require (
 	go.etcd.io/bbolt v1.3.5
 	golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
 	golang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect
-	golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
 	golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
-	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
+	golang.org/x/tools v0.0.0-20201105220310-78b158585360 // indirect
 	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
 	gopkg.in/yaml.v2 v2.3.0
+	honnef.co/go/tools v0.0.1-2020.1.6 // indirect
 )
diff --git a/go.sum b/go.sum
index 2aca9ad..9b904d5 100644
--- a/go.sum
+++ b/go.sum
@@ -1,4 +1,6 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363 h1:o4lAkfETerCnr1kF9/qwkwjICnU+YLHNDCM8h2xj7as=
+dmitri.shuralyov.com/go/generated v0.0.0-20170818220700-b1254a446363/go.mod h1:WG7q7swWsS2f9PYpt5DoEP/EBYWx8We5UoRltn9vJl8=
 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
@@ -43,6 +45,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc=
+github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E=
 github.com/gabriel-vasile/mimetype v1.1.1 h1:qbN9MPuRf3bstHu9zkI9jDWNfH//9+9kHxr9oRBBBOA=
 github.com/gabriel-vasile/mimetype v1.1.1/go.mod h1:6CDPel/o/3/s4+bp6kIbsWATq8pmgOisOPG40CJa6To=
 github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
@@ -79,6 +83,8 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
 github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/google/addlicense v0.0.0-20200906110928-a0294312aa76 h1:JypWNzPMSgH5yL0NvFoAIsDRlKFgL0AsS3GO5bg4Pto=
+github.com/google/addlicense v0.0.0-20200906110928-a0294312aa76/go.mod h1:EMjYTRimagHs1FwlIqKyX3wAM0u3rA+McvlIIWmSamA=
 github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -86,6 +92,9 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.1 h1:JFrFEBb2xKufg6XkJsJr+WbKb4FQlURi5RUcBveYu9k=
 github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=
+github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
 github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gookit/color v1.2.7 h1:4qePMNWZhrmbfYJDix+J4V2l0iVW+6jQGjicELlN14E=
@@ -94,6 +103,8 @@ github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c h1:16eHWuMGvCjSf
 github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherwasm v1.1.0 h1:fA2uLoctU5+T3OhOn2vYP0DVT6pxc7xhTlBB1paATqQ=
 github.com/gopherjs/gopherwasm v1.1.0/go.mod h1:SkZ8z7CWBz5VXbhJel8TxCmAcsQqzgWGR/8nMhyhZSI=
+github.com/gordonklaus/ineffassign v0.0.0-20201107091007-3b93a8888063 h1:dKprcOvlsvqfWn/iGvz+oYuC2axESeSMuF8dDrWMNsE=
+github.com/gordonklaus/ineffassign v0.0.0-20201107091007-3b93a8888063/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
 github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
 github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
 github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
@@ -114,6 +125,8 @@ github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGAR
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
+github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -125,6 +138,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/loov/goda v0.1.3 h1:MJZDekwjQXeez3UC+0D3o9UuKQAsFrfwnSofo8dQxWw=
+github.com/loov/goda v0.1.3/go.mod h1:VbaMyXKSJBooVNPvmai26ee1WhHo1xtj0EAOvSXr2+w=
 github.com/lucasb-eyer/go-colorful v1.0.2/go.mod h1:0MS4r+7BZKSJ5mw4/S5MPN+qHFF1fYclkSPilDOKW0s=
 github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
 github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
@@ -185,6 +200,7 @@ github.com/rivo/tview v0.0.0-20200915114512-42866ecf6ca6/go.mod h1:xV4Aw4WIX8cmh
 github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -226,6 +242,7 @@ github.com/xhit/go-str2duration/v2 v2.0.0 h1:uFtk6FWB375bP7ewQl+/1wBcn840GPhnySO
 github.com/xhit/go-str2duration/v2 v2.0.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/zalando/go-keyring v0.1.0 h1:ffq972Aoa4iHNzBlUHgK5Y+k8+r/8GvcGd80/OFZb/k=
 github.com/zalando/go-keyring v0.1.0/go.mod h1:RaxNwUITJaHVdQ0VC7pELPZ3tOWn13nr0gZMZEhpVU0=
@@ -246,9 +263,13 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de h1:ikNHVSjEfnvz6sxdSPCaPt572qowuyMDMJLLm3Db3ig=
 golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=
 golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
 golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -260,6 +281,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
 golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e h1:3G+cUijn7XD+S4eJFddp53Pv7+slrESplyjG25HgL+k=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
@@ -267,16 +289,12 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
 golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201008141435-b3e1573b7520 h1:Bx6FllMpG4NWDOfhMBz1VR2QYNp/SAOHPIAsaVmxfPo=
-golang.org/x/sync v0.0.0-20201008141435-b3e1573b7520/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -312,14 +330,17 @@ golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxb
 golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd h1:/e+gpKk9r3dJobndpTytxS2gOy6m5uvpg+ISQoEcusQ=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb h1:iKlO7ROJc6SttHKlxzwGytRtBUqX4VARrNTgP2YLX5M=
+golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
 golang.org/x/tools v0.0.0-20200410194907-79a7a3126eef/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20201017001424-6003fad69a88 h1:ZB1XYzdDo7c/O48jzjMkvIjnC120Z9/CwgDWhePjQdQ=
-golang.org/x/tools v0.0.0-20201017001424-6003fad69a88/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
-golang.org/x/tools v0.0.0-20201021000207-d49c4edd7d96 h1:K+nJoPcImWk+ZGPHOKkDocKcQPACCz8usiCiVQYfXsk=
-golang.org/x/tools v0.0.0-20201021000207-d49c4edd7d96/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
+golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174 h1:0rx0F4EjJNbxTuzWe0KjKcIzs+3VEb/Mrs/d1ciNz1c=
+golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201105220310-78b158585360 h1:/9CzsU8hOpnSUCtem1vfWNgsVeCTgkMdx+VE5YIYxnU=
+golang.org/x/tools v0.0.0-20201105220310-78b158585360/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
@@ -342,6 +363,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
@@ -355,4 +377,7 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2020.1.6 h1:W18jzjh8mfPez+AwGLxmOImucz/IFjpNlrKVnaj2YVc=
+honnef.co/go/tools v0.0.1-2020.1.6/go.mod h1:pyyisuGw24ruLjrr1ddx39WE0y9OooInRzEYLhQB2YY=
diff --git a/internal/account.go b/internal/account.go
deleted file mode 100644
index f853832..0000000
--- a/internal/account.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright (c) 2020 BitMaelum Authors
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-// the Software, and to permit persons to whom the Software is furnished to do so,
-// subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-package internal
-
-import (
-	"github.com/bitmaelum/bitmaelum-suite/internal/organisation"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
-)
-
-// AccountInfo represents client account information
-type AccountInfo struct {
-	Default bool   `json:"default"` // Is this the default account
-	Address string `json:"address"` // The address of the account
-
-	Name     string            `json:"name"`     // Full name of the user
-	Settings map[string]string `json:"settings"` // Additional settings that can be user-defined
-
-	// Communication and encryption information
-	PrivKey   bmcrypto.PrivKey         `json:"priv_key"`        // PEM encoded private key
-	PubKey    bmcrypto.PubKey          `json:"pub_key"`         // PEM encoded public key
-	Pow       *proofofwork.ProofOfWork `json:"proof,omitempty"` // Proof of work
-	RoutingID string                   `json:"routing_id"`      // ID of the routing used
-}
-
-// AddressHash will return the hash of the given address
-func (info *AccountInfo) AddressHash() hash.Hash {
-	addr, _ := address.NewAddress(info.Address)
-	return addr.Hash()
-}
-
-// OrganisationInfo represents a organisation configuration for a server
-type OrganisationInfo struct {
-	Addr        string                        `json:"addr"`          // org part from the bitmaelum address
-	FullName    string                        `json:"name"`          // Full name of the organisation
-	PrivKey     bmcrypto.PrivKey              `json:"priv_key"`      // PEM encoded private key
-	PubKey      bmcrypto.PubKey               `json:"pub_key"`       // PEM encoded public key
-	Pow         *proofofwork.ProofOfWork      `json:"pow,omitempty"` // Proof of work
-	Validations []organisation.ValidationType `json:"validations"`   // Validations
-}
-
-// RoutingInfo represents a routing configuration for a server
-type RoutingInfo struct {
-	RoutingID string                   `json:"routing_id"`    // ID
-	PrivKey   bmcrypto.PrivKey         `json:"priv_key"`      // PEM encoded private key
-	PubKey    bmcrypto.PubKey          `json:"pub_key"`       // PEM encoded public key
-	Pow       *proofofwork.ProofOfWork `json:"pow,omitempty"` // Proof of work
-	Route     string                   `json:"route"`         // Route to server
-}
-
-// InfoToOrg converts organisation info to an actual organisation structure
-func InfoToOrg(info OrganisationInfo) (*organisation.Organisation, error) {
-	return &organisation.Organisation{
-		Hash:       hash.New(info.Addr),
-		FullName:   info.FullName,
-		PublicKey:  info.PubKey,
-		Validation: info.Validations,
-	}, nil
-}
diff --git a/internal/encrypt/aes_256_gcm.go b/internal/aes_256_gcm.go
similarity index 96%
rename from internal/encrypt/aes_256_gcm.go
rename to internal/aes_256_gcm.go
index 8c400af..204bc94 100644
--- a/internal/encrypt/aes_256_gcm.go
+++ b/internal/aes_256_gcm.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package encrypt
+package internal
 
 import (
 	"crypto/aes"
@@ -96,6 +96,11 @@ func MessageDecrypt(key []byte, message []byte) ([]byte, error) {
 	return aead.Open(nil, nonce, ciphertext, nil)
 }
 
+// CreateCatalogKey generates a new key that is used for encrypting a catalog
+func CreateCatalogKey() ([]byte, error) {
+	return keyGenerator(keySize)
+}
+
 // CatalogEncrypt encrypts a catalog with a random key.
 func CatalogEncrypt(catalog interface{}) ([]byte, []byte, error) {
 	catalogKey, err := keyGenerator(keySize)
diff --git a/internal/api/account.go b/internal/api/account.go
index 33ce8a1..979de24 100644
--- a/internal/api/account.go
+++ b/internal/api/account.go
@@ -20,8 +20,7 @@
 package api
 
 import (
-	"github.com/bitmaelum/bitmaelum-suite/internal"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
+	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	pow "github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
@@ -47,7 +46,7 @@ func (api *API) GetPublicKey(addr hash.Hash) (string, error) {
 }
 
 // CreateAccount creates new account on server
-func (api *API) CreateAccount(info internal.AccountInfo, token string) error {
+func (api *API) CreateAccount(info vault.AccountInfo, token string) error {
 	type inputCreateAccount struct {
 		Addr        hash.Hash       `json:"address"`
 		UserHash    hash.Hash       `json:"user_hash"`
@@ -57,15 +56,10 @@ func (api *API) CreateAccount(info internal.AccountInfo, token string) error {
 		ProofOfWork pow.ProofOfWork `json:"proof_of_work"`
 	}
 
-	addr, err := address.NewAddress(info.Address)
-	if err != nil {
-		return err
-	}
-
 	input := &inputCreateAccount{
-		Addr:        addr.Hash(),
-		UserHash:    addr.LocalHash(),
-		OrgHash:     addr.OrgHash(),
+		Addr:        info.Address.Hash(),
+		UserHash:    info.Address.LocalHash(),
+		OrgHash:     info.Address.OrgHash(),
 		Token:       token,
 		PublicKey:   info.PubKey,
 		ProofOfWork: *info.Pow,
diff --git a/internal/api/client.go b/internal/api/client.go
index 1a0d627..e45ea2f 100644
--- a/internal/api/client.go
+++ b/internal/api/client.go
@@ -32,7 +32,10 @@ import (
 	"time"
 
 	"github.com/bitmaelum/bitmaelum-suite/internal"
+	"github.com/bitmaelum/bitmaelum-suite/internal/config"
 	"github.com/bitmaelum/bitmaelum-suite/internal/ticket"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/ernesto-jimenez/httplogger"
 )
 
@@ -53,42 +56,43 @@ type ClientOpts struct {
 	Host          string
 	AllowInsecure bool
 	Debug         bool
+	JWTToken      string
 }
 
-// NewAnonymous creates a new client that connects anonymously to a BitMaelum server (normally
-// server-to-server communication)
-func NewAnonymous(opts ClientOpts) (*API, error) {
-	var transport http.RoundTripper
-	transport = &http.Transport{
-		// Allow insecure and self-signed certificates if so configured
-		TLSClientConfig: &tls.Config{InsecureSkipVerify: opts.AllowInsecure},
-	}
-
-	if opts.Debug {
-		// Wrap transport in debug logging
-		transport = httplogger.NewLoggedTransport(transport, internal.NewHTTPLogger())
-	}
+// NewAnonymous creates a new client that connects anonymously to a BitMaelum server (normally server-to-server communications)
+func NewAnonymous(host string) (*API, error) {
+	return NewClient(ClientOpts{
+		Host:          host,
+		AllowInsecure: config.Server.Server.AllowInsecure,
+		Debug:         config.Client.Server.DebugHTTP,
+	})
+}
 
-	api := &API{
-		host: CanonicalHost(opts.Host),
-		client: &http.Client{
-			Transport: transport,
-			Timeout:   15 * time.Second,
-		},
+// NewAuthenticated creates a new client that connects to a BitMaelum Server with specific credentials (normally client-to-server communications)
+func NewAuthenticated(addr address.Address, key *bmcrypto.PrivKey, host string) (*API, error) {
+	// Create token based on the private key of the user
+	jwtToken, err := GenerateJWTToken(addr.Hash(), *key)
+	if err != nil {
+		return nil, err
 	}
 
-	return api, nil
+	return NewClient(ClientOpts{
+		Host:          host,
+		JWTToken:      jwtToken,
+		AllowInsecure: config.Server.Server.AllowInsecure,
+		Debug:         config.Client.Server.DebugHTTP,
+	})
 }
 
-// NewAuthenticated creates a new client that connects with the specified account to a BitMaelum server (normally
-// client-to-server communication)
-func NewAuthenticated(info *internal.AccountInfo, opts ClientOpts) (*API, error) {
-	var jwtToken string
+// NewClient creates a new client based on the given options
+func NewClient(opts ClientOpts) (*API, error) {
 	var transport http.RoundTripper
 
 	transport = &http.Transport{
 		// Allow insecure and self-signed certificates if so configured
-		TLSClientConfig: &tls.Config{InsecureSkipVerify: opts.AllowInsecure},
+		TLSClientConfig: &tls.Config{
+			InsecureSkipVerify: opts.AllowInsecure,
+		},
 	}
 
 	if opts.Debug {
@@ -96,25 +100,14 @@ func NewAuthenticated(info *internal.AccountInfo, opts ClientOpts) (*API, error)
 		transport = httplogger.NewLoggedTransport(transport, internal.NewHTTPLogger())
 	}
 
-	if info != nil {
-		// Create JWT token based on the private key of the user
-		var err error
-		jwtToken, err = internal.GenerateJWTToken(info.AddressHash(), info.PrivKey)
-		if err != nil {
-			return nil, err
-		}
-	}
-
-	api := &API{
+	return &API{
 		host: CanonicalHost(opts.Host),
 		client: &http.Client{
 			Transport: transport,
-			Timeout:   30 * time.Second,
+			Timeout:   15 * time.Second,
 		},
-		jwt: jwtToken,
-	}
-
-	return api, nil
+		jwt: opts.JWTToken,
+	}, nil
 }
 
 // Get gets raw bytes from API
diff --git a/internal/jwt.go b/internal/api/jwt.go
similarity index 99%
rename from internal/jwt.go
rename to internal/api/jwt.go
index 2aa817a..44bcc33 100644
--- a/internal/jwt.go
+++ b/internal/api/jwt.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package internal
+package api
 
 import (
 	"crypto/subtle"
diff --git a/internal/jwt_ed25519_sign.go b/internal/api/jwt_ed25519_sign.go
similarity index 99%
rename from internal/jwt_ed25519_sign.go
rename to internal/api/jwt_ed25519_sign.go
index 0046972..7c57443 100644
--- a/internal/jwt_ed25519_sign.go
+++ b/internal/api/jwt_ed25519_sign.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package internal
+package api
 
 import (
 	"crypto/ed25519"
diff --git a/internal/container/apikey.go b/internal/container/apikey.go
index 56d89a9..4b7fd5a 100644
--- a/internal/container/apikey.go
+++ b/internal/container/apikey.go
@@ -59,5 +59,5 @@ func setupAPIKeyRepo() (interface{}, error) {
 }
 
 func init() {
-	Set("api-key", setupAPIKeyRepo)
+	Instance.SetShared("api-key", setupAPIKeyRepo)
 }
diff --git a/internal/container/authkey.go b/internal/container/authkey.go
index 1a7e171..50e670d 100644
--- a/internal/container/authkey.go
+++ b/internal/container/authkey.go
@@ -64,5 +64,5 @@ func setupAuthKeyRepo() (interface{}, error) {
 }
 
 func init() {
-	Set("auth-key", setupAuthKeyRepo)
+	Instance.SetShared("auth-key", setupAuthKeyRepo)
 }
diff --git a/internal/container/helpers.go b/internal/container/bitmaelum.go
similarity index 63%
rename from internal/container/helpers.go
rename to internal/container/bitmaelum.go
index 809b12d..950f88e 100644
--- a/internal/container/helpers.go
+++ b/internal/container/bitmaelum.go
@@ -20,46 +20,39 @@
 package container
 
 import (
-	"github.com/bitmaelum/bitmaelum-suite/internal/account"
 	"github.com/bitmaelum/bitmaelum-suite/internal/key"
 	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
 	"github.com/bitmaelum/bitmaelum-suite/internal/subscription"
 	"github.com/bitmaelum/bitmaelum-suite/internal/ticket"
 )
 
-/*
- * Simple helpers that makes it easier to fetch services. Normally, these things should be generated by a full-fledged
- * dependency container system I guess. It basically converts the interface{} returned by the container into the
- * correct interface.
- */
-
-// GetAccountRepo will return the current account repository
-func GetAccountRepo() account.Repository {
-	// return Get("account").(func() account.Repository)()
-	return Get("account").(account.Repository)
+// Instance is the main bitmaelum service container
+var Instance = Type{
+	definitions: make(map[string]*ServiceDefinition),
+	resolved:    make(map[string]interface{}),
 }
 
 // GetAPIKeyRepo will return the current api key repository
-func GetAPIKeyRepo() key.APIKeyRepo {
-	return Get("api-key").(key.APIKeyRepo)
+func (c *Type) GetAPIKeyRepo() key.APIKeyRepo {
+	return c.Get("api-key").(key.APIKeyRepo)
 }
 
 // GetAuthKeyRepo will return the current auth key repository
-func GetAuthKeyRepo() key.AuthKeyRepo {
-	return Get("auth-key").(key.AuthKeyRepo)
+func (c *Type) GetAuthKeyRepo() key.AuthKeyRepo {
+	return c.Get("auth-key").(key.AuthKeyRepo)
 }
 
 // GetResolveService will return the current resolver service
-func GetResolveService() *resolver.Service {
-	return Get("resolver").(*resolver.Service)
+func (c *Type) GetResolveService() *resolver.Service {
+	return c.Get("resolver").(*resolver.Service)
 }
 
 // GetSubscriptionRepo will return the current subscription repository
-func GetSubscriptionRepo() subscription.Repository {
-	return Get("subscription").(subscription.Repository)
+func (c *Type) GetSubscriptionRepo() subscription.Repository {
+	return c.Get("subscription").(subscription.Repository)
 }
 
 // GetTicketRepo will return the current ticket repository
-func GetTicketRepo() ticket.Repository {
-	return Get("ticket").(ticket.Repository)
+func (c *Type) GetTicketRepo() ticket.Repository {
+	return c.Get("ticket").(ticket.Repository)
 }
diff --git a/internal/container/container.go b/internal/container/container.go
index 61c3c73..fe926df 100644
--- a/internal/container/container.go
+++ b/internal/container/container.go
@@ -27,43 +27,16 @@ package container
  * There is no functionality for dependencies etc, but we have shared/unshared services.
  */
 
-// Container is the main container structure holding all service
-type Container struct {
-	definitions map[string]*ServiceDefinition
-	resolved    map[string]interface{}
-}
-
-// The main container instance
-var container = Container{
-	definitions: make(map[string]*ServiceDefinition), // List of definitions
-	resolved:    make(map[string]interface{}),        // Resolved definition functions
-}
-
-// Get returns a service from the container
-func Get(key string) interface{} {
-	return container.Get(key)
-}
-
 // ServiceFunc is the function that needs to be resolved in the definition
 type ServiceFunc func() (interface{}, error)
 
-// Set sets a service from the container as a singleton
-func Set(key string, build ServiceFunc) {
-	container.Set(key, ServiceTypeSingle, build)
-}
-
-// SetMulti sets a service from the container. It will return a new instance on each call
-func SetMulti(key string, build ServiceFunc) {
-	container.Set(key, ServiceTypeMulti, build)
-}
-
 // ServiceType defines what kind of service it is (singleton, or new instance on each call)
 type ServiceType int
 
 // Service types
 const (
-	ServiceTypeSingle ServiceType = iota
-	ServiceTypeMulti
+	ServiceTypeShared    ServiceType = iota // Service is shared. Each call returns the same instance
+	ServiceTypeNonShared                    // Service is not shared. Each call returns a new instance
 )
 
 // ServiceDefinition is a single service definition
@@ -72,36 +45,66 @@ type ServiceDefinition struct {
 	Type ServiceType
 }
 
+// Container is the interface each container needs to implement
+type Container interface {
+	SetShared(key string, f ServiceFunc)
+	SetNonShared(key string, f ServiceFunc)
+	Get(key string) interface{}
+	Has(key string) bool
+}
+
+// Type is the main container structure holding all service
+type Type struct {
+	definitions map[string]*ServiceDefinition
+	resolved    map[string]interface{}
+}
+
 // NewContainer will create a new container
 func NewContainer() Container {
-	c := Container{
+	return &Type{
 		definitions: make(map[string]*ServiceDefinition),
 		resolved:    make(map[string]interface{}),
 	}
+}
+
+// SetNonShared will set the function for the given service. It is not shared, meaning you will get a new instance on each call to get
+func (c *Type) SetNonShared(key string, f ServiceFunc) {
+	c.definitions[key] = &ServiceDefinition{
+		Func: f,
+		Type: ServiceTypeNonShared,
+	}
 
-	return c
+	// Delete existing resolved object if any
+	delete(c.resolved, key)
 }
 
-// Set will set the function for the given service
-func (c Container) Set(key string, t ServiceType, f ServiceFunc) {
+// SetShared will set the function for the given service. It will return a shared instance on each call to get
+func (c *Type) SetShared(key string, f ServiceFunc) {
 	c.definitions[key] = &ServiceDefinition{
 		Func: f,
-		Type: t,
+		Type: ServiceTypeShared,
 	}
 
 	// Delete existing resolved object if any
 	delete(c.resolved, key)
 }
 
+// Has will return true when the definition exists
+func (c *Type) Has(key string) bool {
+	_, ok := c.definitions[key]
+
+	return ok
+}
+
 // Get will retrieve the function for the given service, or nil when not found
-func (c Container) Get(key string) interface{} {
+func (c *Type) Get(key string) interface{} {
 	s, ok := c.definitions[key]
 	if !ok {
 		return nil
 	}
 
 	// Multi means we don't use a shared instance but instead instantiate a new object each time called
-	if s.Type == ServiceTypeMulti {
+	if s.Type == ServiceTypeNonShared {
 		obj, err := s.Func()
 		if err != nil {
 			return nil
diff --git a/internal/container/resolver.go b/internal/container/resolver.go
index af39d3a..5074c79 100644
--- a/internal/container/resolver.go
+++ b/internal/container/resolver.go
@@ -69,5 +69,5 @@ func getSQLiteRepository(dsn string) (resolver.Repository, error) {
 }
 
 func init() {
-	Set("resolver", setupResolverService)
+	Instance.SetShared("resolver", setupResolverService)
 }
diff --git a/internal/container/subscription.go b/internal/container/subscription.go
index 0f39658..4619120 100644
--- a/internal/container/subscription.go
+++ b/internal/container/subscription.go
@@ -59,5 +59,5 @@ func setupSubscriptionRepo() (interface{}, error) {
 }
 
 func init() {
-	Set("subscription", setupSubscriptionRepo)
+	Instance.SetShared("subscription", setupSubscriptionRepo)
 }
diff --git a/internal/container/ticket.go b/internal/container/ticket.go
index b18ef91..b8db215 100644
--- a/internal/container/ticket.go
+++ b/internal/container/ticket.go
@@ -59,5 +59,5 @@ func setupTicketRepo() (interface{}, error) {
 }
 
 func init() {
-	Set("ticket", setupTicketRepo)
+	Instance.SetShared("ticket", setupTicketRepo)
 }
diff --git a/internal/key/permissions.go b/internal/key_permissions.go
similarity index 92%
rename from internal/key/permissions.go
rename to internal/key_permissions.go
index e7f9f22..df2ff6a 100644
--- a/internal/key/permissions.go
+++ b/internal/key_permissions.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package key
+package internal
 
 const (
 	// PermFlush Permission to restart/reload the system including flushing/forcing the queues
@@ -30,8 +30,8 @@ const (
 	PermGetHeaders string = "get-headers"
 )
 
-// ManagementPermissons is a list of all permissions available for remote management
-var ManagementPermissons = []string{
+// ManagementPermissions is a list of all permissions available for remote management
+var ManagementPermissions = []string{
 	PermAPIKeys,
 	PermFlush,
 	PermGenerateInvites,
diff --git a/internal/message/catalog.go b/internal/message/catalog.go
index a99e4b5..6e44aa4 100644
--- a/internal/message/catalog.go
+++ b/internal/message/catalog.go
@@ -21,13 +21,14 @@ package message
 
 import (
 	"errors"
+	"fmt"
 	"io"
+	"os"
 	"path"
+	"strings"
 	"time"
 
 	"github.com/bitmaelum/bitmaelum-suite/internal"
-	"github.com/bitmaelum/bitmaelum-suite/internal/compress"
-	"github.com/bitmaelum/bitmaelum-suite/internal/encrypt"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
@@ -102,15 +103,17 @@ type Block struct {
 }
 
 // NewCatalog initialises a new catalog. This catalog has to be filled with more info, blocks and attachments
-func NewCatalog(info *internal.AccountInfo) *Catalog {
+func NewCatalog(sender, recipient *address.Address, subject string) *Catalog {
 	c := &Catalog{}
 
 	c.CreatedAt = time.Now()
+	c.From.Address = sender.String()
+	// c.From.Name = info.Name
+	// c.From.ProofOfWork = info.Pow
+	// c.From.PublicKey = &info.PubKey
 
-	c.From.Address = info.Address
-	c.From.Name = info.Name
-	c.From.ProofOfWork = info.Pow
-	c.From.PublicKey = &info.PubKey
+	c.Subject = subject
+	c.To.Address = recipient.String()
 
 	return c
 }
@@ -143,18 +146,18 @@ func (c *Catalog) AddBlock(entry Block) error {
 
 	// Very arbitrary size on when we should compress output first
 	if entry.Size >= 1024 {
-		reader = compress.ZlibCompress(entry.Reader)
+		reader = ZlibCompress(entry.Reader)
 		compression = "zlib"
 	}
 
 	// Generate key iv for this block
-	iv, key, err := encrypt.GenerateIvAndKey()
+	iv, key, err := internal.GenerateIvAndKey()
 	if err != nil {
 		return err
 	}
 
 	// Wrap reader with encryption reader
-	reader, err = encrypt.GetAesEncryptorReader(iv, key, reader)
+	reader, err = internal.GetAesEncryptorReader(iv, key, reader)
 	if err != nil {
 		return err
 	}
@@ -197,18 +200,18 @@ func (c *Catalog) AddAttachment(entry Attachment) error {
 
 	// Very arbitrary size on when we should compress output first
 	if stats.Size() >= 1024 {
-		reader = compress.ZlibCompress(entry.Reader)
+		reader = ZlibCompress(entry.Reader)
 		compression = "zlib"
 	}
 
 	// Generate Key and IV that we will use for encryption
-	iv, key, err := encrypt.GenerateIvAndKey()
+	iv, key, err := internal.GenerateIvAndKey()
 	if err != nil {
 		return err
 	}
 
 	// Wrap our reader with the encryption reader
-	reader, err = encrypt.GetAesEncryptorReader(iv, key, reader)
+	reader, err = internal.GetAesEncryptorReader(iv, key, reader)
 	if err != nil {
 		return err
 	}
@@ -255,3 +258,68 @@ func (c *Catalog) GetBlock(blockType string) (*BlockType, error) {
 func (c *Catalog) GetFirstBlock() *BlockType {
 	return &c.Blocks[0]
 }
+
+// GenerateBlocks generates blocks that can be added to a catalog
+func GenerateBlocks(b []string) ([]Block, error) {
+	// Parse blocks
+	var blocks []Block
+	for _, block := range b {
+		split := strings.SplitN(block, ",", 2)
+		if len(split) <= 1 {
+			return nil, fmt.Errorf("please specify blocks in the format '<type>,<content>' or '<type>,file:<filename>'")
+		}
+
+		// By default assume content is inline
+		size := int64(len(split[1]))
+		var r io.Reader = strings.NewReader(split[1])
+
+		if strings.HasPrefix(split[1], "file:") {
+			// Open file as a reader
+			f, err := os.Open(strings.TrimPrefix(split[1], "file:"))
+			if err != nil {
+				return nil, err
+			}
+
+			// Read file size
+			fi, err := f.Stat()
+			if err != nil {
+				return nil, err
+			}
+
+			r = f
+			size = fi.Size()
+		}
+
+		blocks = append(blocks, Block{
+			Type:   split[0],
+			Size:   uint64(size),
+			Reader: r,
+		})
+	}
+
+	return blocks, nil
+}
+
+// GenerateAttachments creates message attachments that we can add to a catalog
+func GenerateAttachments(a []string) ([]Attachment, error) {
+	// Parse attachments
+	var attachments []Attachment
+	for _, attachment := range a {
+		_, err := os.Stat(attachment)
+		if os.IsNotExist(err) {
+			return nil, fmt.Errorf("attachment %s does not exist", attachment)
+		}
+
+		reader, err := os.Open(attachment)
+		if err != nil {
+			return nil, fmt.Errorf("attachment %s cannot be opened", attachment)
+		}
+
+		attachments = append(attachments, Attachment{
+			Path:   attachment,
+			Reader: reader,
+		})
+	}
+
+	return attachments, nil
+}
diff --git a/internal/message/compose.go b/internal/message/compose.go
new file mode 100644
index 0000000..d9b97d2
--- /dev/null
+++ b/internal/message/compose.go
@@ -0,0 +1,102 @@
+// Copyright (c) 2020 BitMaelum Authors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+package message
+
+import (
+	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
+)
+
+// Compose will create a new message and places it inside an envelope. This can be used for actual sending the message
+func Compose(addressing Addressing, subject string, b, a []string) (*Envelope, error) {
+	cat, err := generateCatalog(addressing.Sender.Address, addressing.Recipient.Address, subject, b, a)
+	if err != nil {
+		return nil, err
+	}
+
+	header, err := generateHeader(addressing.Sender.Address, addressing.Recipient.Address)
+	if err != nil {
+		return nil, err
+	}
+
+	envelope, err := NewEnvelope()
+	if err != nil {
+		return nil, err
+	}
+
+	err = envelope.AddCatalog(cat)
+	if err != nil {
+		return nil, err
+	}
+
+	err = envelope.AddHeader(header)
+	if err != nil {
+		return nil, err
+	}
+
+	// Close the envelope for sending
+	err = envelope.CloseAndEncrypt(addressing.Sender.PrivKey, addressing.Recipient.PubKey)
+	if err != nil {
+		return nil, err
+	}
+
+	return envelope, nil
+}
+
+// Generate a header file based on the info provided
+func generateHeader(sender, recipient address.Address) (*Header, error) {
+	header := &Header{}
+
+	header.To.Addr = sender.Hash()
+	header.From.Addr = recipient.Hash()
+
+	return header, nil
+}
+
+// Generate a catalog filled with blocks and attachments
+func generateCatalog(sender, recipient address.Address, subject string, b, a []string) (*Catalog, error) {
+	// Create a new catalog
+	cat := NewCatalog(&sender, &recipient, subject)
+
+	// Add blocks to catalog
+	blocks, err := GenerateBlocks(b)
+	if err != nil {
+		return nil, err
+	}
+	for _, block := range blocks {
+		err := cat.AddBlock(block)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	// Add attachments to catalog
+	attachments, err := GenerateAttachments(a)
+	if err != nil {
+		return nil, err
+	}
+	for _, attachment := range attachments {
+		err := cat.AddAttachment(attachment)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	return cat, nil
+}
diff --git a/internal/compress/compress.go b/internal/message/compress.go
similarity index 98%
rename from internal/compress/compress.go
rename to internal/message/compress.go
index 94e1cc0..76dfc6e 100644
--- a/internal/compress/compress.go
+++ b/internal/message/compress.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package compress
+package message
 
 import (
 	"compress/zlib"
diff --git a/pkg/bmcrypto/encrypt.go b/internal/message/encrypt.go
similarity index 73%
rename from pkg/bmcrypto/encrypt.go
rename to internal/message/encrypt.go
index 43b93bf..05fdd0a 100644
--- a/pkg/bmcrypto/encrypt.go
+++ b/internal/message/encrypt.go
@@ -17,28 +17,29 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package bmcrypto
+package message
 
 import (
 	"crypto/rand"
 	"crypto/rsa"
 	"errors"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/encrypt"
+	"github.com/bitmaelum/bitmaelum-suite/internal"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 )
 
 // Encrypt a message with the given key
-func Encrypt(pubKey PubKey, message []byte) ([]byte, string, string, error) {
+func Encrypt(pubKey bmcrypto.PubKey, message []byte) ([]byte, string, string, error) {
 	if !pubKey.CanEncrypt() && !pubKey.CanKeyExchange() {
 		return nil, "", "", errors.New("this key type is not usable for encryption")
 	}
 
 	switch pubKey.Type {
-	case KeyTypeRSA, KeyTypeRSAV1:
+	case bmcrypto.KeyTypeRSA, bmcrypto.KeyTypeRSAV1:
 		encryptedMessage, err := encryptRsa(pubKey.K.(*rsa.PublicKey), message)
 		return encryptedMessage, "", "rsa+aes256gcm", err
 		// TODO: Implement KeyTypeECDSA
-	case KeyTypeED25519:
+	case bmcrypto.KeyTypeED25519:
 		return encryptED25519(pubKey, message)
 	}
 
@@ -46,16 +47,16 @@ func Encrypt(pubKey PubKey, message []byte) ([]byte, string, string, error) {
 }
 
 // Decrypt a message with the given key
-func Decrypt(key PrivKey, txID string, message []byte) ([]byte, error) {
+func Decrypt(key bmcrypto.PrivKey, txID string, message []byte) ([]byte, error) {
 	if !key.CanEncrypt() && !key.CanKeyExchange() {
 		return nil, errors.New("this key type is not usable for encryption")
 	}
 
 	switch key.Type {
-	case KeyTypeRSA, KeyTypeRSAV1:
+	case bmcrypto.KeyTypeRSA, bmcrypto.KeyTypeRSAV1:
 		return decryptRsa(key.K.(*rsa.PrivateKey), message)
 	// TODO: Implement KeyTypeECDSA
-	case KeyTypeED25519:
+	case bmcrypto.KeyTypeED25519:
 		return decryptED25519(key, txID, message)
 
 	}
@@ -71,26 +72,26 @@ func decryptRsa(key *rsa.PrivateKey, message []byte) ([]byte, error) {
 	return rsa.DecryptPKCS1v15(rand.Reader, key, message)
 }
 
-func encryptED25519(pubKey PubKey, message []byte) ([]byte, string, string, error) {
-	secret, txID, err := DualKeyExchange(pubKey)
+func encryptED25519(pubKey bmcrypto.PubKey, message []byte) ([]byte, string, string, error) {
+	secret, txID, err := bmcrypto.DualKeyExchange(pubKey)
 	if err != nil {
 		return nil, "", "", err
 	}
 
-	encryptedMessage, err := encrypt.MessageEncrypt(secret, message)
+	encryptedMessage, err := internal.MessageEncrypt(secret, message)
 	return encryptedMessage, txID.ToHex(), "ed25519+aes256gcm", err
 }
 
-func decryptED25519(privKey PrivKey, txIDString string, message []byte) ([]byte, error) {
-	txID, err := TxIDFromString(txIDString)
+func decryptED25519(privKey bmcrypto.PrivKey, txIDString string, message []byte) ([]byte, error) {
+	txID, err := bmcrypto.TxIDFromString(txIDString)
 	if err != nil {
 		return nil, err
 	}
 
-	secret, ok, err := DualKeyGetSecret(privKey, *txID)
+	secret, ok, err := bmcrypto.DualKeyGetSecret(privKey, *txID)
 	if !ok || err != nil {
 		return nil, err
 	}
 
-	return encrypt.MessageDecrypt(secret, message)
+	return internal.MessageDecrypt(secret, message)
 }
diff --git a/internal/message/envelope.go b/internal/message/envelope.go
new file mode 100644
index 0000000..0c62987
--- /dev/null
+++ b/internal/message/envelope.go
@@ -0,0 +1,174 @@
+// Copyright (c) 2020 BitMaelum Authors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULeAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+package message
+
+import (
+	"bytes"
+	"errors"
+	"io"
+
+	"github.com/bitmaelum/bitmaelum-suite/internal"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
+)
+
+var errAlreadyClosed = errors.New("envelope is already closed and encrypted")
+
+// Envelope is a simple structure that will keep a header and catalog together.
+type Envelope struct {
+	Header            *Header               // The message header
+	EncryptedCatalog  []byte                // The catalog in encrypted bytes
+	BlockReaders      map[string]*io.Reader // Readers for the blocks
+	AttachmentReaders map[string]*io.Reader // Readers for the attachments
+
+	closed     bool     // True when the envelope is closed (and encrypted)
+	catalog    *Catalog // The opened catalog
+	catalogKey []byte   // The key used for encryption
+}
+
+// Addressing is the configuration for an envelope (@TODO: bad naming)
+type Addressing struct {
+	Sender struct {
+		Address address.Address   // Address of the sender
+		PrivKey *bmcrypto.PrivKey // Private key of the sender
+		Host    string            // Host address of the sender mail server
+	}
+	Recipient struct {
+		Address address.Address  // Address of the recipient
+		PubKey  *bmcrypto.PubKey // Public key of the recipient
+	}
+}
+
+// NewAddressing sets up a new addressing struct that can be used for composing and sending a message
+func NewAddressing(senderAddress address.Address, senderPrivKey *bmcrypto.PrivKey, host string, recipientAddress address.Address, recipientPubKey *bmcrypto.PubKey) Addressing {
+	return Addressing{
+		Sender: struct {
+			Address address.Address
+			PrivKey *bmcrypto.PrivKey
+			Host    string
+		}{
+			Address: senderAddress,
+			PrivKey: senderPrivKey,
+			Host:    host,
+		},
+		Recipient: struct {
+			Address address.Address
+			PubKey  *bmcrypto.PubKey
+		}{
+			Address: recipientAddress,
+			PubKey:  recipientPubKey,
+		},
+	}
+}
+
+// NewEnvelope creates a new (open) envelope which is used for holding a complete message
+func NewEnvelope() (*Envelope, error) {
+	var err error
+
+	envelope := &Envelope{
+		closed:            false,
+		BlockReaders:      make(map[string]*io.Reader),
+		AttachmentReaders: make(map[string]*io.Reader),
+	}
+
+	// Always create a catalog key for later use
+	envelope.catalogKey, err = internal.CreateCatalogKey()
+	if err != nil {
+		return nil, err
+	}
+
+	return envelope, nil
+}
+
+// AddHeader will add a header to the envelope
+func (e *Envelope) AddHeader(hdr *Header) error {
+	if e.closed {
+		return errAlreadyClosed
+	}
+
+	e.Header = hdr
+	return nil
+}
+
+// AddCatalog will add a catalog to the envelope
+func (e *Envelope) AddCatalog(cat *Catalog) error {
+	if e.closed {
+		return errAlreadyClosed
+	}
+
+	e.catalog = cat
+
+	// Remove all readers first, then add all the new ones
+	e.BlockReaders = make(map[string]*io.Reader)
+	e.AttachmentReaders = make(map[string]*io.Reader)
+
+	for _, block := range cat.Blocks {
+		e.BlockReaders[block.ID] = &block.Reader
+	}
+
+	for _, att := range cat.Attachments {
+		e.AttachmentReaders[att.ID] = &att.Reader
+	}
+
+	return nil
+}
+
+// CloseAndEncrypt will close an envelope, and make sure all settings are set correctly for sending the message
+func (e *Envelope) CloseAndEncrypt(senderPrivKey *bmcrypto.PrivKey, recipientPubKey *bmcrypto.PubKey) error {
+	var err error
+
+	if e.closed {
+		return errAlreadyClosed
+	}
+
+	// Encrypt the catalog
+	e.EncryptedCatalog, err = internal.JSONEncrypt(e.catalogKey, e.catalog)
+	if err != nil {
+		return err
+	}
+
+	// Calculate checksums of the encrypted catalog
+	r := bytes.NewBuffer(e.EncryptedCatalog)
+	e.Header.Catalog.Checksum, err = CalculateChecksums(r)
+	if err != nil {
+		return err
+	}
+
+	// Set catalog information in the header
+	e.Header.Catalog.Size = uint64(len(e.EncryptedCatalog))
+	ek, tx, cr, err := Encrypt(*recipientPubKey, e.catalogKey)
+	if err != nil {
+		return err
+	}
+	e.Header.Catalog.EncryptedKey = ek
+	e.Header.Catalog.TransactionID = tx
+	e.Header.Catalog.Crypto = cr
+
+	// Sign the header
+	err = SignClientHeader(e.Header, *senderPrivKey)
+	if err != nil {
+		return err
+	}
+
+	// All done. Close the envelope, and remove the open catalog
+	e.catalog = nil
+	e.closed = true
+	return nil
+}
diff --git a/internal/client/signature.go b/internal/message/signature.go
similarity index 59%
rename from internal/client/signature.go
rename to internal/message/signature.go
index cffa0e0..e52dad3 100644
--- a/internal/client/signature.go
+++ b/internal/message/signature.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package client
+package message
 
 import (
 	"crypto/sha256"
@@ -25,14 +25,73 @@ import (
 	"encoding/json"
 	"fmt"
 
+	"github.com/bitmaelum/bitmaelum-suite/internal/config"
 	"github.com/bitmaelum/bitmaelum-suite/internal/container"
-	"github.com/bitmaelum/bitmaelum-suite/internal/message"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 )
 
-// SignHeader will add a client signature to a message header. This can be used to proof the origin of the message
-func SignHeader(header *message.Header, privKey bmcrypto.PrivKey) error {
+// SignServerHeader will add a server signature to a message header. This can be used to proof the origin of the message
+func SignServerHeader(header *Header) error {
+	// Already signed? Then skip
+	if len(header.Signatures.Server) > 0 {
+		return nil
+	}
+
+	data, err := json.Marshal(header)
+	if err != nil {
+		return err
+	}
+
+	h := sha256.Sum256(data)
+	sig, err := bmcrypto.Sign(config.Routing.PrivateKey, h[:])
+	if err != nil {
+		return err
+	}
+
+	header.Signatures.Server = base64.StdEncoding.EncodeToString(sig)
+	return nil
+}
+
+// VerifyServerHeader will verify a server signature from a message header. This can be used to proof the origin of the message
+func VerifyServerHeader(header Header) bool {
+	// Fetch public key from routing
+	rs := container.Instance.GetResolveService()
+	addr, err := rs.ResolveAddress(header.From.Addr)
+	if err != nil {
+		return false
+	}
+
+	// No header at all
+	if len(header.Signatures.Server) == 0 {
+		return false
+	}
+
+	// Store signature
+	targetSignature, err := base64.StdEncoding.DecodeString(header.Signatures.Server)
+	if err != nil {
+		return false
+	}
+	header.Signatures.Server = ""
+
+	// Generate hash
+	data, err := json.Marshal(&header)
+	if err != nil {
+		return false
+	}
+	h := sha256.Sum256(data)
+
+	// Verify signature
+	ok, err := bmcrypto.Verify(addr.RoutingInfo.PublicKey, h[:], []byte(targetSignature))
+	if err != nil {
+		return false
+	}
+
+	return ok
+}
+
+// SignClientHeader will add a client signature to a message header. This can be used to proof the origin of the message
+func SignClientHeader(header *Header, privKey bmcrypto.PrivKey) error {
 	// Already signed? Then skip
 	if len(header.Signatures.Client) > 0 {
 		fmt.Println("already signed")
@@ -54,11 +113,10 @@ func SignHeader(header *message.Header, privKey bmcrypto.PrivKey) error {
 	return nil
 }
 
-// VerifyHeader will verify a client signature from a message header. This can be used to proof the origin of the message
-func VerifyHeader(header message.Header) bool {
+// VerifyClientHeader will verify a client signature from a message header. This can be used to proof the origin of the message
+func VerifyClientHeader(header Header) bool {
 	// Fetch public key from routing
-	rs := container.GetResolveService()
-
+	rs := container.Instance.GetResolveService()
 	addr, err := rs.ResolveAddress(header.From.Addr)
 	if err != nil {
 		return false
@@ -86,7 +144,7 @@ func VerifyHeader(header message.Header) bool {
 
 	// If we have sent an authorized key key, we need to validate this first
 	pubKey := addr.PublicKey
-	if header.From.SignedBy == message.SignedByTypeAuthorized {
+	if header.From.SignedBy == SignedByTypeAuthorized {
 		pubKey = *header.AuthorizedBy.PublicKey
 
 		// Verify our authorized key
diff --git a/internal/messages/send.go b/internal/messages/send.go
new file mode 100644
index 0000000..32712a1
--- /dev/null
+++ b/internal/messages/send.go
@@ -0,0 +1,75 @@
+// Copyright (c) 2020 BitMaelum Authors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+package messages
+
+import (
+	"errors"
+
+	"github.com/bitmaelum/bitmaelum-suite/internal/api"
+	"github.com/bitmaelum/bitmaelum-suite/internal/message"
+	"golang.org/x/sync/errgroup"
+)
+
+// Send will send a message inside envelope via routing, using addr/key
+func Send(client api.API, envelope *message.Envelope) error {
+
+	// Get upload ticket
+	t, err := client.GetTicket(envelope.Header.From.Addr, envelope.Header.To.Addr, "")
+	if err != nil {
+		return errors.New("cannot get ticket from server: " + err.Error())
+	}
+	if !t.Valid {
+		return errors.New("invalid ticket returned by server")
+	}
+
+	// parallelize uploads
+	g := new(errgroup.Group)
+	g.Go(func() error {
+		return client.UploadHeader(*t, envelope.Header)
+	})
+	g.Go(func() error {
+		return client.UploadCatalog(*t, envelope.EncryptedCatalog)
+	})
+	for id, r := range envelope.BlockReaders {
+		// Store locally, otherwise the anonymous go function doesn't know which "id" and "r"
+		id := id
+		r := r
+		g.Go(func() error {
+			return client.UploadBlock(*t, id, *r)
+		})
+	}
+	for id, r := range envelope.AttachmentReaders {
+		// Store locally, otherwise the anonymous go function doesn't know which "id" and "r"
+		id := id
+		r := r
+		g.Go(func() error {
+			return client.UploadBlock(*t, id, *r)
+		})
+	}
+
+	// Wait until all are completed
+	if err := g.Wait(); err != nil {
+		_ = client.DeleteMessage(*t)
+		return err
+	}
+
+	// All done, mark upload as completed
+	return client.CompleteUpload(*t)
+}
diff --git a/internal/mnemonic.go b/internal/mnemonic.go
index 0b95060..489fbe3 100644
--- a/internal/mnemonic.go
+++ b/internal/mnemonic.go
@@ -59,7 +59,6 @@ func GenerateRSAKeypairFromMnemonic(mnemonic string, keyType bmcrypto.KeyType) (
 		bits = bmcrypto.RsaBits[1]
 	}
 
-
 	return genRSAKey(e, bits)
 }
 
diff --git a/internal/parse/parse_args.go b/internal/parse_args.go
similarity index 81%
rename from internal/parse/parse_args.go
rename to internal/parse_args.go
index 943dd45..2c43af7 100644
--- a/internal/parse/parse_args.go
+++ b/internal/parse_args.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package parse
+package internal
 
 import (
 	"errors"
@@ -25,7 +25,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/key"
 	"github.com/xhit/go-str2duration/v2"
 )
 
@@ -48,13 +47,13 @@ func ValidDuration(ds string) (time.Duration, error) {
 	return vd, nil
 }
 
-// ManagementPermissions checks all permission and returns an error when a permission is not valid
-func ManagementPermissions(perms []string) error {
+// CheckManagementPermissions checks all permission and returns an error when a permission is not valid
+func CheckManagementPermissions(perms []string) error {
 	for _, p := range perms {
 		p = strings.ToLower(p)
 
 		found := false
-		for _, ap := range key.ManagementPermissons {
+		for _, ap := range ManagementPermissions {
 			if p == ap {
 				found = true
 			}
@@ -67,13 +66,13 @@ func ManagementPermissions(perms []string) error {
 	return nil
 }
 
-// AccountPermissions checks all permission and returns an error when a permission is not valid
-func AccountPermissions(perms []string) error {
+// CheckAccountPermissions checks all permission and returns an error when a permission is not valid
+func CheckAccountPermissions(perms []string) error {
 	for _, p := range perms {
 		p = strings.ToLower(p)
 
 		found := false
-		for _, ap := range key.AccountPermissions {
+		for _, ap := range AccountPermissions {
 			if p == ap {
 				found = true
 			}
diff --git a/internal/resolver/service.go b/internal/resolver/service.go
index 28778c5..05d72d5 100644
--- a/internal/resolver/service.go
+++ b/internal/resolver/service.go
@@ -24,9 +24,8 @@ import (
 	"encoding/base64"
 	"strconv"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/organisation"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
+	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	lru "github.com/hashicorp/golang-lru"
@@ -133,14 +132,9 @@ func (s *Service) ResolveOrganisation(orgHash hash.Hash) (*OrganisationInfo, err
 }
 
 // UploadAddressInfo uploads resolve information to one (or more) resolvers
-func (s *Service) UploadAddressInfo(info internal.AccountInfo, orgToken string) error {
-	addr, err := address.NewAddress(info.Address)
-	if err != nil {
-		return err
-	}
-
-	return s.repo.UploadAddress(*addr, &AddressInfo{
-		Hash:      info.AddressHash().String(),
+func (s *Service) UploadAddressInfo(info vault.AccountInfo, orgToken string) error {
+	return s.repo.UploadAddress(info.Address, &AddressInfo{
+		Hash:      info.Address.Hash().String(),
 		PublicKey: info.PubKey,
 		RoutingID: info.RoutingID,
 		Pow:       info.Pow.String(),
@@ -148,16 +142,12 @@ func (s *Service) UploadAddressInfo(info internal.AccountInfo, orgToken string)
 }
 
 // UploadRoutingInfo uploads resolve information to one (or more) resolvers
-func (s *Service) UploadRoutingInfo(info internal.RoutingInfo) error {
-	return s.repo.UploadRouting(&RoutingInfo{
-		Hash:      info.RoutingID,
-		PublicKey: info.PubKey,
-		Routing:   info.Route,
-	}, info.PrivKey)
+func (s *Service) UploadRoutingInfo(info RoutingInfo, privKey *bmcrypto.PrivKey) error {
+	return s.repo.UploadRouting(&info, *privKey)
 }
 
 // UploadOrganisationInfo uploads resolve information to one (or more) resolvers
-func (s *Service) UploadOrganisationInfo(info internal.OrganisationInfo) error {
+func (s *Service) UploadOrganisationInfo(info vault.OrganisationInfo) error {
 	return s.repo.UploadOrganisation(&OrganisationInfo{
 		Hash:        hash.New(info.Addr).String(),
 		PublicKey:   info.PubKey,
diff --git a/internal/server/signature.go b/internal/server/signature.go
deleted file mode 100644
index ee03e6b..0000000
--- a/internal/server/signature.go
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright (c) 2020 BitMaelum Authors
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-// the Software, and to permit persons to whom the Software is furnished to do so,
-// subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-package server
-
-import (
-	"crypto/sha256"
-	"encoding/base64"
-	"encoding/json"
-
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
-	"github.com/bitmaelum/bitmaelum-suite/internal/message"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
-)
-
-// SignHeader will add a server signature to a message header. This can be used to proof the origin of the message
-func SignHeader(header *message.Header) error {
-	// Already signed? Then skip
-	if len(header.Signatures.Server) > 0 {
-		return nil
-	}
-
-	data, err := json.Marshal(header)
-	if err != nil {
-		return err
-	}
-
-	h := sha256.Sum256(data)
-	sig, err := bmcrypto.Sign(config.Routing.PrivateKey, h[:])
-	if err != nil {
-		return err
-	}
-
-	header.Signatures.Server = base64.StdEncoding.EncodeToString(sig)
-	return nil
-}
-
-// VerifyHeader will verify a server signature from a message header. This can be used to proof the origin of the message
-func VerifyHeader(header message.Header) bool {
-	// Fetch public key from routing
-	rs := container.GetResolveService()
-	addr, err := rs.ResolveAddress(header.From.Addr)
-	if err != nil {
-		return false
-	}
-
-	// No header at all
-	if len(header.Signatures.Server) == 0 {
-		return false
-	}
-
-	// Store signature
-	targetSignature, err := base64.StdEncoding.DecodeString(header.Signatures.Server)
-	if err != nil {
-		return false
-	}
-	header.Signatures.Server = ""
-
-	// Generate hash
-	data, err := json.Marshal(&header)
-	if err != nil {
-		return false
-	}
-	h := sha256.Sum256(data)
-
-	// Verify signature
-	ok, err := bmcrypto.Verify(addr.RoutingInfo.PublicKey, h[:], []byte(targetSignature))
-	if err != nil {
-		return false
-	}
-
-	return ok
-}
diff --git a/internal/invite/signature.go b/internal/signature/invite.go
similarity index 99%
rename from internal/invite/signature.go
rename to internal/signature/invite.go
index 9560901..ce38908 100644
--- a/internal/invite/signature.go
+++ b/internal/signature/invite.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package invite
+package signature
 
 import (
 	"crypto/sha256"
diff --git a/internal/vault/account.go b/internal/vault/account.go
index 8408a3c..b0eb3eb 100644
--- a/internal/vault/account.go
+++ b/internal/vault/account.go
@@ -22,12 +22,28 @@ package vault
 import (
 	"errors"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
 )
 
+// AccountInfo represents client account information
+type AccountInfo struct {
+	Default bool            `json:"default"` // Is this the default account
+	Address address.Address `json:"address"` // The address of the account
+
+	Name     string            `json:"name"`     // Full name of the user
+	Settings map[string]string `json:"settings"` // Additional settings that can be user-defined
+
+	// Communication and encryption information
+	PrivKey   bmcrypto.PrivKey         `json:"priv_key"`        // PEM encoded private key
+	PubKey    bmcrypto.PubKey          `json:"pub_key"`         // PEM encoded public key
+	Pow       *proofofwork.ProofOfWork `json:"proof,omitempty"` // Proof of work
+	RoutingID string                   `json:"routing_id"`      // ID of the routing used
+}
+
 // AddAccount adds a new account to the vault
-func (v *Vault) AddAccount(account internal.AccountInfo) {
+func (v *Vault) AddAccount(account AccountInfo) {
 	v.Store.Accounts = append(v.Store.Accounts, account)
 }
 
@@ -35,7 +51,7 @@ func (v *Vault) AddAccount(account internal.AccountInfo) {
 func (v *Vault) RemoveAccount(addr address.Address) {
 	k := 0
 	for _, acc := range v.Store.Accounts {
-		if acc.Address != addr.String() {
+		if acc.Address.String() != addr.String() {
 			v.Store.Accounts[k] = acc
 			k++
 		}
@@ -44,9 +60,9 @@ func (v *Vault) RemoveAccount(addr address.Address) {
 }
 
 // GetAccountInfo tries to find the given address and returns the account from the vault
-func (v *Vault) GetAccountInfo(addr address.Address) (*internal.AccountInfo, error) {
+func (v *Vault) GetAccountInfo(addr address.Address) (*AccountInfo, error) {
 	for i := range v.Store.Accounts {
-		if v.Store.Accounts[i].Address == addr.String() {
+		if v.Store.Accounts[i].Address.String() == addr.String() {
 			return &v.Store.Accounts[i], nil
 		}
 	}
@@ -63,7 +79,7 @@ func (v *Vault) HasAccount(addr address.Address) bool {
 
 // GetDefaultAccount returns the default account from the vault. This could be the one set to default, or if none found,
 // the first account in the vault. Returns nil when no accounts are present in the vault.
-func (v *Vault) GetDefaultAccount() *internal.AccountInfo {
+func (v *Vault) GetDefaultAccount() *AccountInfo {
 	// No accounts, return nil
 	if len(v.Store.Accounts) == 0 {
 		return nil
diff --git a/internal/vault/organisation.go b/internal/vault/organisation.go
index 909c3c6..5ff3626 100644
--- a/internal/vault/organisation.go
+++ b/internal/vault/organisation.go
@@ -22,17 +22,39 @@ package vault
 import (
 	"errors"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
+	"github.com/bitmaelum/bitmaelum-suite/internal/organisation"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
 )
 
+// OrganisationInfo represents a organisation configuration for a server
+type OrganisationInfo struct {
+	Addr        string                        `json:"addr"`          // org part from the bitmaelum address
+	FullName    string                        `json:"name"`          // Full name of the organisation
+	PrivKey     bmcrypto.PrivKey              `json:"priv_key"`      // PEM encoded private key
+	PubKey      bmcrypto.PubKey               `json:"pub_key"`       // PEM encoded public key
+	Pow         *proofofwork.ProofOfWork      `json:"pow,omitempty"` // Proof of work
+	Validations []organisation.ValidationType `json:"validations"`   // Validations
+}
+
+// ToOrg converts organisation info to an actual organisation structure
+func (info OrganisationInfo) ToOrg() *organisation.Organisation {
+	return &organisation.Organisation{
+		Hash:       hash.New(info.Addr),
+		FullName:   info.FullName,
+		PublicKey:  info.PubKey,
+		Validation: info.Validations,
+	}
+}
+
 // AddOrganisation adds an organisation to the vault
-func (v *Vault) AddOrganisation(organisation internal.OrganisationInfo) {
+func (v *Vault) AddOrganisation(organisation OrganisationInfo) {
 	v.Store.Organisations = append(v.Store.Organisations, organisation)
 }
 
 // GetOrganisationInfo tries to find the given organisation and returns the organisation from the vault
-func (v *Vault) GetOrganisationInfo(orgHash hash.Hash) (*internal.OrganisationInfo, error) {
+func (v *Vault) GetOrganisationInfo(orgHash hash.Hash) (*OrganisationInfo, error) {
 
 	for i := range v.Store.Organisations {
 		h := hash.New(v.Store.Organisations[i].Addr)
diff --git a/internal/vault/vault.go b/internal/vault/vault.go
index 31df658..5cee8c5 100644
--- a/internal/vault/vault.go
+++ b/internal/vault/vault.go
@@ -34,7 +34,6 @@ import (
 	"path/filepath"
 	"strings"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
 	"github.com/bitmaelum/bitmaelum-suite/internal/console"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
@@ -60,8 +59,8 @@ var VaultPassword string
 
 // StoreType hold the actual data that is encrypted inside the vault
 type StoreType struct {
-	Accounts      []internal.AccountInfo      `json:"accounts"`
-	Organisations []internal.OrganisationInfo `json:"organisations"`
+	Accounts      []AccountInfo      `json:"accounts"`
+	Organisations []OrganisationInfo `json:"organisations"`
 }
 
 // Vault defines our vault with path and password. Only the accounts should be exported
@@ -87,8 +86,8 @@ func New(p string, pwd []byte) (*Vault, error) {
 
 	v := &Vault{
 		Store: StoreType{
-			Accounts:      []internal.AccountInfo{},
-			Organisations: []internal.OrganisationInfo{},
+			Accounts:      []AccountInfo{},
+			Organisations: []OrganisationInfo{},
 		},
 		RawData:  []byte{},
 		password: pwd,
@@ -219,11 +218,11 @@ func (v *Vault) DecryptContainer(container *vaultContainer) error {
 
 	if container.Version == VersionV0 {
 		// Unmarshal "old" style, with no organisations present
-		var accounts []internal.AccountInfo
+		var accounts []AccountInfo
 		err = json.Unmarshal(plainText, &accounts)
 		if err == nil {
 			v.Store.Accounts = accounts
-			v.Store.Organisations = []internal.OrganisationInfo{}
+			v.Store.Organisations = []OrganisationInfo{}
 
 			// Write back to disk in a newer format
 			return v.WriteToDisk()
@@ -314,7 +313,7 @@ func (v *Vault) FindShortRoutingID(id string) string {
 
 // GetAccountOrDefault find the address from the vault. If address is empty, it will fetch the default address, or the
 // first address in the vault if no default address is present.
-func GetAccountOrDefault(vault *Vault, a string) *internal.AccountInfo {
+func GetAccountOrDefault(vault *Vault, a string) *AccountInfo {
 	if a == "" {
 		return vault.GetDefaultAccount()
 	}
@@ -324,7 +323,7 @@ func GetAccountOrDefault(vault *Vault, a string) *internal.AccountInfo {
 }
 
 // GetAccount returns the given account, or nil when not found
-func GetAccount(vault *Vault, a string) (*internal.AccountInfo, error) {
+func GetAccount(vault *Vault, a string) (*AccountInfo, error) {
 	addr, err := address.NewAddress(a)
 	if err != nil {
 		return nil, err
diff --git a/pkg/bmcrypto/generate.go b/pkg/bmcrypto/generate.go
index c6b80db..765f739 100644
--- a/pkg/bmcrypto/generate.go
+++ b/pkg/bmcrypto/generate.go
@@ -28,8 +28,9 @@ import (
 )
 
 var (
-	curveFunc = elliptic.P384     // Curve used for ECDSA
-	RsaBits   = []int{2048, 4096} // RSA key sizes
+	curveFunc = elliptic.P384 // Curve used for ECDSA
+	// RsaBits is an array with the given RSA versions and their corresponding bit sizes
+	RsaBits = []int{2048, 4096} // RSA key sizes
 )
 
 // GenerateKeyPair generates a private/public keypair based on the given type
diff --git a/pkg/bmcrypto/keys.go b/pkg/bmcrypto/keys.go
index 7fae16d..f64c1c1 100644
--- a/pkg/bmcrypto/keys.go
+++ b/pkg/bmcrypto/keys.go
@@ -33,12 +33,15 @@ import (
 	"strings"
 )
 
+// KeyType defines a type for a given key in a certain configuration (like: rsa 2048 or rsa 4096 etc)
 type KeyType string
 
+// String returns the textual representation of the keytype
 func (kt KeyType) String() string {
 	return string(kt)
 }
 
+// Keytype constants
 const (
 	KeyTypeRSA     KeyType = "rsa"
 	KeyTypeECDSA   KeyType = "ecdsa"
diff --git a/tools/README.md b/tools/README.md
new file mode 100644
index 0000000..16dc311
--- /dev/null
+++ b/tools/README.md
@@ -0,0 +1,14 @@
+Various tools. Most of them are used for development purposes and should not be used for normal daily use.
+
+check-org               Checks validation of an organisation
+hash-address            Displays the hash for the given address
+jwt                     Generates a JWT token for authentication to a mailserver
+jwt-validate            Validates a JWT token
+proof-of-work           Does proof of work for X bits on given data. 
+readmail                Reads the given mail
+resolve                 Resolves an address, routing or organisation
+resolve-auth            Generates authentication key for updating / deleting resolver key
+toaster                 Checks mailbox and displays a graphical toast message if new mail arrives
+update-pow              Updates proof-of-work for all accounts to X bits.
+update-resolver         Uploads organisation or account to the resolver
+vault-edit              Allows you to manually edit the vault's JSON data and automatically encrypts it back again.
diff --git a/tools/check-org/README.md b/tools/check-org/README.md
deleted file mode 100644
index 7162f73..0000000
--- a/tools/check-org/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tool: check-org
-
-Checks validation of an organisation  
diff --git a/tools/hash-address/README.md b/tools/hash-address/README.md
deleted file mode 100644
index f490ce9..0000000
--- a/tools/hash-address/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tool: hash-address
-
-Converts a message address to its hash.  
diff --git a/tools/jwt-validate/README.md b/tools/jwt-validate/README.md
deleted file mode 100644
index bb0aad2..0000000
--- a/tools/jwt-validate/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tool: jwt
-
-Generates a JWT token for authentication to a mailserver
diff --git a/tools/jwt-validate/main.go b/tools/jwt-validate/main.go
index 6f78a02..835a46a 100644
--- a/tools/jwt-validate/main.go
+++ b/tools/jwt-validate/main.go
@@ -24,7 +24,7 @@ import (
 	"os"
 	"strings"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
+	"github.com/bitmaelum/bitmaelum-suite/internal/api"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/davecgh/go-spew/spew"
@@ -57,7 +57,7 @@ func main() {
 	}
 	tokenString := auth[7:]
 
-	token, err := internal.ValidateJWTToken(tokenString, addr.Hash(), *key)
+	token, err := api.ValidateJWTToken(tokenString, addr.Hash(), *key)
 	if err == nil {
 		fmt.Printf("Token validated correctly")
 		spew.Dump(token)
diff --git a/tools/jwt/README.md b/tools/jwt/README.md
deleted file mode 100644
index bb0aad2..0000000
--- a/tools/jwt/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tool: jwt
-
-Generates a JWT token for authentication to a mailserver
diff --git a/tools/jwt/main.go b/tools/jwt/main.go
index 04a1137..b9427e7 100644
--- a/tools/jwt/main.go
+++ b/tools/jwt/main.go
@@ -23,6 +23,7 @@ import (
 	"fmt"
 
 	"github.com/bitmaelum/bitmaelum-suite/internal"
+	"github.com/bitmaelum/bitmaelum-suite/internal/api"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
@@ -65,14 +66,14 @@ func main() {
 	fmt.Printf("%s\n", info.PubKey)
 	fmt.Println("")
 
-	ts, err := internal.GenerateJWTToken(fromAddr.Hash(), info.PrivKey)
+	ts, err := api.GenerateJWTToken(fromAddr.Hash(), info.PrivKey)
 	if err != nil {
 		logrus.Fatal(err)
 	}
 	fmt.Println(ts)
 	fmt.Println("")
 
-	token, err := internal.ValidateJWTToken(ts, fromAddr.Hash(), info.PubKey)
+	token, err := api.ValidateJWTToken(ts, fromAddr.Hash(), info.PubKey)
 	if err != nil {
 		logrus.Fatal(err)
 	}
diff --git a/tools/migrate-hash/README.md b/tools/migrate-hash/README.md
deleted file mode 100644
index 5bc0a97..0000000
--- a/tools/migrate-hash/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tool: migrate-hash
-
-Migrates old hashes to new double-hashed addresses
diff --git a/tools/migrate-hash/main.go b/tools/migrate-hash/main.go
deleted file mode 100644
index 619be46..0000000
--- a/tools/migrate-hash/main.go
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (c) 2020 BitMaelum Authors
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-// the Software, and to permit persons to whom the Software is furnished to do so,
-// subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-package main
-
-import (
-	"os"
-
-	"github.com/bitmaelum/bitmaelum-suite/internal"
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
-	"github.com/olekukonko/tablewriter"
-)
-
-type options struct {
-	Config   string `short:"c" long:"config" description:"Path to your configuration file"`
-	Password string `short:"p" long:"password" description:"Vault password" default:""`
-}
-
-var opts options
-
-func main() {
-	internal.ParseOptions(&opts)
-	config.LoadClientConfig(opts.Config)
-
-	vault.VaultPassword = opts.Password
-	v := vault.OpenVault()
-
-	table := tablewriter.NewWriter(os.Stdout)
-	table.SetHeader([]string{"Address", "New"})
-
-	for _, acc := range v.Store.Accounts {
-		addr, err := address.NewAddress(acc.Address)
-		if err != nil {
-			panic(err)
-		}
-
-		values := []string{
-			addr.String(),
-			addr.Hash().String(),
-		}
-
-		table.Append(values)
-	}
-
-	table.Render()
-}
diff --git a/tools/proof-of-work/README.md b/tools/proof-of-work/README.md
deleted file mode 100644
index bbf5852..0000000
--- a/tools/proof-of-work/README.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Tool: proof-of-work
-
-Does proof of work for X bits on given data. 
-
-Returns the actual proof.
-
-Usage: ./proof-of-work 22 jay!
-
-to generate 22 bits of proof for the string "jay!" 
diff --git a/tools/readmail/main.go b/tools/readmail/main.go
index 2515bfd..0aa64e9 100644
--- a/tools/readmail/main.go
+++ b/tools/readmail/main.go
@@ -27,12 +27,9 @@ import (
 
 	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/encrypt"
 	"github.com/bitmaelum/bitmaelum-suite/internal/message"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
-	hash2 "github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 )
 
 type options struct {
@@ -67,8 +64,7 @@ func main() {
 	if err != nil {
 		panic(err)
 	}
-	hash := hash2.New(info.Address)
-	fmt.Printf("Reading message for user %s (%s) (%s)\n", info.Name, info.Address, hash.String())
+	fmt.Printf("Reading message for user %s (%s) (%s)\n", info.Name, info.Address, info.Address.Hash().String())
 
 	data, err := ioutil.ReadFile(opts.Path + "/header.json")
 	if err != nil {
@@ -90,13 +86,13 @@ func main() {
 		panic(err)
 	}
 
-	decryptedKey, err := bmcrypto.Decrypt(info.PrivKey, header.Catalog.TransactionID, header.Catalog.EncryptedKey)
+	decryptedKey, err := message.Decrypt(info.PrivKey, header.Catalog.TransactionID, header.Catalog.EncryptedKey)
 	if err != nil {
 		panic(err)
 	}
 
 	catalog := &message.Catalog{}
-	err = encrypt.CatalogDecrypt(decryptedKey, data, catalog)
+	err = internal.CatalogDecrypt(decryptedKey, data, catalog)
 	if err != nil {
 		panic(err)
 	}
@@ -111,15 +107,15 @@ func main() {
 			panic(err)
 		}
 
-		r, err := encrypt.GetAesDecryptorReader(block.IV, block.Key, f)
+		r, err := internal.GetAesDecryptorReader(block.IV, block.Key, f)
 		if err != nil {
-			f.Close()
+			_ = f.Close()
 			panic(err)
 		}
 
 		content, err := ioutil.ReadAll(r)
 		if err != nil {
-			f.Close()
+			_ = f.Close()
 			panic(err)
 		}
 
diff --git a/tools/resolve-auth/README.md b/tools/resolve-auth/README.md
deleted file mode 100644
index 3286772..0000000
--- a/tools/resolve-auth/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tool: resolve-auth
-
-Generates authentication key for updating / deleting resolver key
diff --git a/tools/resolve-auth/main.go b/tools/resolve-auth/main.go
index 4361849..fdfae76 100644
--- a/tools/resolve-auth/main.go
+++ b/tools/resolve-auth/main.go
@@ -28,7 +28,6 @@ import (
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/sirupsen/logrus"
 )
 
@@ -51,8 +50,7 @@ func main() {
 		os.Exit(1)
 	}
 
-	ha := hash.New(info.Address)
-	hashed := []byte(ha.String() + info.RoutingID)
+	hashed := []byte(info.Address.Hash().String() + info.RoutingID)
 	sig, err := bmcrypto.Sign(info.PrivKey, hashed)
 	if err != nil {
 		logrus.Fatal("Cannot sign")
diff --git a/tools/resolve/README.md b/tools/resolve/README.md
deleted file mode 100644
index 909c4b1..0000000
--- a/tools/resolve/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tool: resolve
-
-Resolves a BitMaelum address and displays info
diff --git a/tools/resolve/main.go b/tools/resolve/main.go
index 3f198e5..4adc575 100644
--- a/tools/resolve/main.go
+++ b/tools/resolve/main.go
@@ -61,7 +61,7 @@ func resolveAddress(addr string) {
 	}
 	addrHash := a.Hash()
 
-	svc := container.GetResolveService()
+	svc := container.Instance.GetResolveService()
 	info, err := svc.ResolveAddress(addrHash)
 	if err != nil {
 		logrus.Fatal(err)
@@ -74,7 +74,7 @@ func resolveAddress(addr string) {
 }
 
 func resolveRouting(routingID string) {
-	svc := container.GetResolveService()
+	svc := container.Instance.GetResolveService()
 	info, err := svc.ResolveRouting(routingID)
 	if err != nil {
 		logrus.Fatal(err)
@@ -88,7 +88,7 @@ func resolveRouting(routingID string) {
 func resolveOrganisation(orgAddr string) {
 	orgHash := hash.New(orgAddr)
 
-	svc := container.GetResolveService()
+	svc := container.Instance.GetResolveService()
 	info, err := svc.ResolveOrganisation(orgHash)
 	if err != nil {
 		logrus.Fatal(err)
diff --git a/tools/toaster/main.go b/tools/toaster/main.go
index cb14205..af483db 100644
--- a/tools/toaster/main.go
+++ b/tools/toaster/main.go
@@ -52,7 +52,7 @@ func main() {
 		log.Fatal(err)
 	}
 
-	r := container.GetResolveService()
+	r := container.Instance.GetResolveService()
 	info, err := r.ResolveAddress(addr.Hash())
 	if err != nil {
 		log.Fatal(err)
diff --git a/tools/update-pow/README.md b/tools/update-pow/README.md
deleted file mode 100644
index a55d299..0000000
--- a/tools/update-pow/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tool: update-pow
-
-Updates proof-of-work for accounts to X bits.
diff --git a/tools/update-pow/main.go b/tools/update-pow/main.go
index 663fe69..3cdc15c 100644
--- a/tools/update-pow/main.go
+++ b/tools/update-pow/main.go
@@ -25,7 +25,6 @@ import (
 	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/internal/config"
 	"github.com/bitmaelum/bitmaelum-suite/internal/vault"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
 )
 
@@ -49,14 +48,8 @@ func main() {
 		var pow *proofofwork.ProofOfWork
 
 		if len(v.Store.Accounts[i].Pow.Data) == 0 {
-			var err error
-			addr, err := address.NewAddress(v.Store.Accounts[i].Address)
-			if err != nil {
-				panic(err)
-			}
-
 			// proof of work is actually our hash address
-			v.Store.Accounts[i].Pow.Data = addr.Hash().String()
+			v.Store.Accounts[i].Pow.Data = v.Store.Accounts[i].Address.Hash().String()
 		}
 
 		if v.Store.Accounts[i].Pow.Bits >= opts.Bits && v.Store.Accounts[i].Pow.IsValid() && !opts.Force {
diff --git a/tools/update-resolver/README.md b/tools/update-resolver/README.md
deleted file mode 100644
index 21eeb80..0000000
--- a/tools/update-resolver/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Tool: update-resolver
-
-Update some records to the resolver
diff --git a/tools/update-resolver/main.go b/tools/update-resolver/main.go
index 298a91e..c4b8029 100644
--- a/tools/update-resolver/main.go
+++ b/tools/update-resolver/main.go
@@ -62,7 +62,7 @@ func main() {
 			os.Exit(1)
 		}
 
-		rs := container.GetResolveService()
+		rs := container.Instance.GetResolveService()
 		err = rs.UploadAddressInfo(*info, "")
 		if err != nil {
 			fmt.Printf("Error for account %s: %s\n", info.Address, err)
@@ -77,7 +77,7 @@ func main() {
 			os.Exit(1)
 		}
 
-		rs := container.GetResolveService()
+		rs := container.Instance.GetResolveService()
 		err := rs.UploadOrganisationInfo(*info)
 		if err != nil {
 			fmt.Printf("Error for organisation %s: %s\n", info.Addr, err)


Test patch:
diff --git a/cmd/bm-server/middleware/auth/auth_apikey_test.go b/cmd/bm-server/middleware/auth/auth_apikey_test.go
index c8276ec..625ebf7 100644
--- a/cmd/bm-server/middleware/auth/auth_apikey_test.go
+++ b/cmd/bm-server/middleware/auth/auth_apikey_test.go
@@ -25,9 +25,9 @@ import (
 	"testing"
 	"time"
 
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/account"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/middleware"
-	"github.com/bitmaelum/bitmaelum-suite/internal/account"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/key"
 	testing2 "github.com/bitmaelum/bitmaelum-suite/internal/testing"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
@@ -48,11 +48,11 @@ func TestAuthAPIKeyAuthenticate(t *testing.T) {
 	_, pubkey, err := testing2.ReadTestKey("../../../../testdata/key-ed25519-1.json")
 	assert.NoError(t, err)
 
-	container.Set("account", func() (interface{}, error) {
+	container.Instance.SetShared("account", func() (interface{}, error) {
 		return account.NewMockRepository(), nil
 	})
 
-	accountRepo := container.GetAccountRepo()
+	accountRepo := container.Instance.GetAccountRepo()
 	// container.Set("account", func() interface{} { return accountRepo })
 	_ = accountRepo.Create(hash.New("example!"), *pubkey)
 	_ = accountRepo.Create(hash.New("user-1!"), *pubkey)
@@ -63,7 +63,7 @@ func TestAuthAPIKeyAuthenticate(t *testing.T) {
 	// 42 creates BMK-dl2INvNSQTZ5zQu9MxNmGyAVmNkB33io
 	rand.Seed(42)
 	apiKeyRepo := key.NewAPIMockRepository()
-	container.Set("api-key", func() (interface{}, error) { return apiKeyRepo, nil })
+	container.Instance.SetShared("api-key", func() (interface{}, error) { return apiKeyRepo, nil })
 	for _, k := range apiKeyFixtures {
 		// Create a new key, so it will randomize through our seed
 		nk := key.NewAPIAccountKey(*k.AddressHash, k.Permissions, k.Expires, k.Desc)
@@ -156,7 +156,7 @@ func checkTrue(t *testing.T, a middleware.Authenticator, req *http.Request, hash
 	assert.Equal(t, hash, ctx.Value(AddressContext))
 }
 
-func checkKey(t *testing.T, a APIKeyAuth, pass bool, token, addr, routeName string) {
+func checkKey(t *testing.T, a APIKeyAuth, shouldPass bool, token, addr, routeName string) {
 	req, _ := http.NewRequest("GET", "/foo", nil)
 	req.Header.Set("authorization", "bearer "+token)
 	req = mux.SetURLVars(req, map[string]string{
@@ -164,7 +164,7 @@ func checkKey(t *testing.T, a APIKeyAuth, pass bool, token, addr, routeName stri
 	})
 
 	ctx, ok := a.Authenticate(req, routeName)
-	if pass {
+	if shouldPass {
 		assert.True(t, ok)
 		assert.NotNil(t, ctx)
 		// Check token in context
diff --git a/cmd/bm-server/middleware/auth/auth_jwt_test.go b/cmd/bm-server/middleware/auth/auth_jwt_test.go
index db25fb2..a81b142 100644
--- a/cmd/bm-server/middleware/auth/auth_jwt_test.go
+++ b/cmd/bm-server/middleware/auth/auth_jwt_test.go
@@ -25,8 +25,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/account"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/account"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	testing2 "github.com/bitmaelum/bitmaelum-suite/internal/testing"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/gorilla/mux"
@@ -40,11 +40,11 @@ func TestAuthJwtAuthenticate(t *testing.T) {
 	_, pubkey, err := testing2.ReadTestKey("../../../../testdata/key-ed25519-1.json")
 	assert.NoError(t, err)
 
-	container.Set("account", func() (interface{}, error) {
+	container.Instance.SetShared("account", func() (interface{}, error) {
 		return account.NewMockRepository(), nil
 	})
 
-	accountRepo := container.GetAccountRepo()
+	accountRepo := container.Instance.GetAccountRepo()
 	_ = accountRepo.Create(hash.New("example!"), *pubkey)
 
 	jwt.TimeFunc = func() time.Time {
diff --git a/cmd/bm-server/middleware/auth/auth_onbehalf_jwt_test.go b/cmd/bm-server/middleware/auth/auth_onbehalf_jwt_test.go
index e4eca75..3df7af0 100644
--- a/cmd/bm-server/middleware/auth/auth_onbehalf_jwt_test.go
+++ b/cmd/bm-server/middleware/auth/auth_onbehalf_jwt_test.go
@@ -24,8 +24,8 @@ import (
 	"testing"
 	"time"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal/account"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/account"
+	"github.com/bitmaelum/bitmaelum-suite/cmd/bm-server/internal/container"
 	"github.com/bitmaelum/bitmaelum-suite/internal/key"
 	testing2 "github.com/bitmaelum/bitmaelum-suite/internal/testing"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
@@ -55,20 +55,20 @@ func TestOnbehalfAuthJwtAuthenticate(t *testing.T) {
 	_, pubkey3, err := testing2.ReadTestKey("../../../../testdata/key-ed25519-3.json")
 	assert.NoError(t, err)
 
-	container.Set("account", func() (interface{}, error) {
+	container.Instance.SetShared("account", func() (interface{}, error) {
 		return account.NewMockRepository(), nil
 	})
-	container.Set("auth-key", func() (interface{}, error) {
+	container.Instance.SetShared("auth-key", func() (interface{}, error) {
 		return key.NewAuthMockRepository(), nil
 	})
 
-	accountRepo := container.GetAccountRepo()
+	accountRepo := container.Instance.GetAccountRepo()
 	_ = accountRepo.Create(hash.New("example!"), *pubkey1)
 
 	// Sign key 2 with key 1
 	ak := key.NewAuthKey(hash.New("example!"), pubkey2, "", time.Date(2020, 01, 01, 13, 00, 00, 0, time.UTC), "my desc")
 	_ = ak.Sign(*privkey1)
-	authRepo := container.GetAuthKeyRepo()
+	authRepo := container.Instance.GetAuthKeyRepo()
 	_ = authRepo.Store(ak)
 
 	// Sign key 3 with key 1
diff --git a/internal/encrypt/aes_256_gcm_test.go b/internal/aes_256_gcm_test.go
similarity index 99%
rename from internal/encrypt/aes_256_gcm_test.go
rename to internal/aes_256_gcm_test.go
index 2ab6416..2e597fd 100644
--- a/internal/encrypt/aes_256_gcm_test.go
+++ b/internal/aes_256_gcm_test.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package encrypt
+package internal
 
 import (
 	"bytes"
diff --git a/internal/jwt_ed25519_sign_test.go b/internal/api/jwt_ed25519_sign_test.go
similarity index 94%
rename from internal/jwt_ed25519_sign_test.go
rename to internal/api/jwt_ed25519_sign_test.go
index 14fde99..ae25d5f 100644
--- a/internal/jwt_ed25519_sign_test.go
+++ b/internal/api/jwt_ed25519_sign_test.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package internal
+package api
 
 import (
 	"crypto/ed25519"
@@ -35,7 +35,7 @@ func TestSigningMethodEdDSAAlg(t *testing.T) {
 func TestSigningMethodEdDSASign(t *testing.T) {
 	m := &SigningMethodEdDSA{}
 
-	privKey, pubKey, _ := testing2.ReadTestKey("../testdata/key-ed25519-1.json")
+	privKey, pubKey, _ := testing2.ReadTestKey("../../testdata/key-ed25519-1.json")
 
 	s, err := m.Sign("foobar", privKey.K.(ed25519.PrivateKey))
 	assert.NoError(t, err)
diff --git a/internal/jwt_test.go b/internal/api/jwt_test.go
similarity index 96%
rename from internal/jwt_test.go
rename to internal/api/jwt_test.go
index c4f49ab..6eb4a15 100644
--- a/internal/jwt_test.go
+++ b/internal/api/jwt_test.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package internal
+package api
 
 import (
 	"io/ioutil"
@@ -41,7 +41,7 @@ func TestGenerateJWTToken(t *testing.T) {
 		return time.Date(2020, 01, 01, 12, 34, 56, 0, time.UTC)
 	}
 
-	data, _ := ioutil.ReadFile("../testdata/privkey.rsa")
+	data, _ := ioutil.ReadFile("../../testdata/privkey.rsa")
 	privKey, err := bmcrypto.NewPrivKey(string(data))
 	assert.Nil(t, err)
 
@@ -53,7 +53,7 @@ func TestGenerateJWTToken(t *testing.T) {
 }
 
 func TestValidateJwtTokenExpiry(t *testing.T) {
-	data, _ := ioutil.ReadFile("../testdata/pubkey.rsa")
+	data, _ := ioutil.ReadFile("../../testdata/pubkey.rsa")
 	pubKey, _ := bmcrypto.NewPubKey(string(data))
 	haddr := hash.New("test!")
 
@@ -100,7 +100,7 @@ func TestValidateJWTToken(t *testing.T) {
 		return time.Date(2020, 01, 01, 12, 34, 56, 0, time.UTC)
 	}
 
-	data, _ := ioutil.ReadFile("../testdata/pubkey.rsa")
+	data, _ := ioutil.ReadFile("../../testdata/pubkey.rsa")
 	pubKey, _ := bmcrypto.NewPubKey(string(data))
 
 	haddr := hash.New("test!")
diff --git a/internal/client/signature_test.go b/internal/client/signature_test.go
deleted file mode 100644
index 1bb892c..0000000
--- a/internal/client/signature_test.go
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (c) 2020 BitMaelum Authors
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-// the Software, and to permit persons to whom the Software is furnished to do so,
-// subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-package client
-
-import (
-	"testing"
-
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
-	"github.com/bitmaelum/bitmaelum-suite/internal/message"
-	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
-	testing2 "github.com/bitmaelum/bitmaelum-suite/internal/testing"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
-	"github.com/stretchr/testify/assert"
-)
-
-const (
-	expectedSignature = "lIqI1QYBRHl7yRW367Lx2n/PFadrYDZ2a2NGSaL40EKum0ncOIXs8CIqKZ+LCUgmK2a9iH2d3mbXVPwZ3PBGsVgReaomyG6NrDbZ0PCbgnjmrmkVAFV0bDHlOxUl/BzyV+seIL7FL0lu+cODaHkmzH16FsZ5Vqcf1/Qe2GR/0Ka6xbWcIcajGsKtTx+WtGeZGZ5oLbAFatEjiv5gMAn2umKpP+w7uKhPa6CsYkv2YMVw+z/1NU2CO0jE6/2muihF9x4nPw6yiy+sXP86B26FQXLBcMgTZ4TAtzr/b2KvcEDj8y8HISs/YHJvTdqAXzYTPnha37ZIIZ7ce27Z41GAUQ=="
-)
-
-func TestSignHeader(t *testing.T) {
-	privKey := setup()
-
-	header := &message.Header{}
-	_ = testing2.ReadJSON("../../testdata/header-001.json", &header)
-	assert.Empty(t, header.Signatures.Client)
-	err := SignHeader(header, *privKey)
-	assert.NoError(t, err)
-	assert.Equal(t, expectedSignature, header.Signatures.Client)
-
-	// Already present, don't overwrite
-	_ = testing2.ReadJSON("../../testdata/header-001.json", &header)
-	assert.NotEmpty(t, header.Signatures.Client)
-	header.Signatures.Client = "foobar"
-	err = SignHeader(header, *privKey)
-	assert.NoError(t, err)
-
-	assert.Equal(t, "foobar", header.Signatures.Client)
-}
-
-func TestVerifyHeader(t *testing.T) {
-	privKey := setup()
-
-	header := &message.Header{}
-	_ = testing2.ReadJSON("../../testdata/header-001.json", &header)
-	assert.Empty(t, header.Signatures.Client)
-	err := SignHeader(header, *privKey)
-	assert.NoError(t, err)
-	assert.Equal(t, expectedSignature, header.Signatures.Client)
-
-	// All is ok
-	ok := VerifyHeader(*header)
-	assert.True(t, ok)
-
-	// Incorrect decoding
-	header.Signatures.Client = "A"
-	ok = VerifyHeader(*header)
-	assert.False(t, ok)
-
-	// Empty sig is not ok
-	header.Signatures.Client = ""
-	ok = VerifyHeader(*header)
-	assert.False(t, ok)
-
-	// incorrect key
-	header.Signatures.Client = "Zm9vYmFy"
-	ok = VerifyHeader(*header)
-	assert.False(t, ok)
-}
-
-func setup() *bmcrypto.PrivKey {
-	addr, _ := address.NewAddress("foobar!")
-
-	// Setup container with mock repository for routing
-	repo, _ := resolver.NewMockRepository()
-	container.Set("resolver", func() (interface{}, error) {
-		return resolver.KeyRetrievalService(repo), nil
-	})
-
-	pow := proofofwork.NewWithoutProof(1, "foobar")
-	var (
-		ai resolver.AddressInfo
-		ri resolver.RoutingInfo
-	)
-
-	privKey, pubKey, err := testing2.ReadTestKey("../../testdata/key-1.json")
-	if err != nil {
-		panic(err)
-	}
-	ai = resolver.AddressInfo{
-		RoutingID:   "87654321",
-		PublicKey:   *pubKey,
-		RoutingInfo: resolver.RoutingInfo{},
-		Pow:         pow.String(),
-		Hash:        "000000000000000000000000000097026f0daeaec1aeb8351b096637679cf350",
-	}
-	_ = repo.UploadAddress(*addr, &ai, *privKey, *pow, "")
-
-	ri = resolver.RoutingInfo{
-		PublicKey: *pubKey,
-		Routing:   "127.0.0.1",
-		Hash:      "12345678",
-	}
-
-	_ = repo.UploadRouting(&ri, *privKey)
-
-	// Note: our sender uses key3
-	privKey, pubKey, err = testing2.ReadTestKey("../../testdata/key-3.json")
-	if err != nil {
-		panic(err)
-	}
-
-	ai = resolver.AddressInfo{
-		RoutingID:   "12345678",
-		RoutingInfo: resolver.RoutingInfo{},
-		PublicKey:   *pubKey,
-		Hash:        "000000000000000000018f66a0f3591a883f2b9cc3e95a497e7cf9da1071b4cc",
-		Pow:         pow.String(),
-	}
-	_ = repo.UploadAddress(*addr, &ai, *privKey, *pow, "")
-
-	return privKey
-}
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 364bfd4..95d6f13 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -22,6 +22,7 @@ package config
 import (
 	"io/ioutil"
 	"os"
+	"runtime"
 	"testing"
 
 	"github.com/sirupsen/logrus"
@@ -62,20 +63,26 @@ func TestClientConfig(t *testing.T) {
 	assert.NoError(t, err)
 	assert.Equal(t, 22, Client.Accounts.ProofOfWork)
 
-	Client.Accounts.ProofOfWork = 0
-	err = LoadClientConfigOrPass("")
-	assert.Error(t, err)
-	assert.Equal(t, 0, Client.Accounts.ProofOfWork)
+	// Read from searchpath
+	if runtime.GOOS != "windows" {
+		// This test fails on windows. It expects the file to be on the searchpath, but it isn't because
+		// the searchpath for windows is different. HOwever, we expect a regular path like /etc/bitmaelum/*.yml
+		// for this test to succeed.
+		Client.Accounts.ProofOfWork = 0
+		err = LoadClientConfigOrPass("")
+		assert.NoError(t, err)
+		assert.Equal(t, 22, Client.Accounts.ProofOfWork)
+	}
 
 	// Read from non-existing env
 	Client.Accounts.ProofOfWork = 0
-	os.Setenv("BITMAELUM_CLIENT_CONFIG", "/etc/does/not/exist.yml")
+	_ = os.Setenv("BITMAELUM_CLIENT_CONFIG", "/etc/does/not/exist.yml")
 	err = LoadClientConfigOrPass("")
 	assert.Error(t, err)
 
 	// Read from existing env
 	Client.Accounts.ProofOfWork = 0
-	os.Setenv("BITMAELUM_CLIENT_CONFIG", "/etc/bitmaelum/client-config.yml")
+	_ = os.Setenv("BITMAELUM_CLIENT_CONFIG", "/etc/bitmaelum/client-config.yml")
 	err = LoadClientConfigOrPass("")
 	assert.NoError(t, err)
 	assert.Equal(t, 22, Client.Accounts.ProofOfWork)
@@ -111,20 +118,26 @@ func TestServerConfig(t *testing.T) {
 	assert.Equal(t, 22, Server.Accounts.ProofOfWork)
 
 	// Read from predetermined paths
-	Server.Accounts.ProofOfWork = 0
-	err = LoadServerConfigOrPass("")
-	assert.Error(t, err)
-	assert.Equal(t, 0, Server.Accounts.ProofOfWork)
+	if runtime.GOOS != "windows" {
+		// This test fails on windows. It expects the file to be on the searchpath, but it isn't because
+		// the searchpath for windows is different. HOwever, we expect a regular path like /etc/bitmaelum/*.yml
+		// for this test to succeed.
+
+		Server.Accounts.ProofOfWork = 0
+		err = LoadServerConfigOrPass("")
+		assert.NoError(t, err)
+		assert.Equal(t, 22, Server.Accounts.ProofOfWork)
+	}
 
 	// Read from non-existing env
 	Server.Accounts.ProofOfWork = 0
-	os.Setenv("BITMAELUM_SERVER_CONFIG", "/etc/does/not/exist.yml")
+	_ = os.Setenv("BITMAELUM_SERVER_CONFIG", "/etc/does/not/exist.yml")
 	err = LoadServerConfigOrPass("")
 	assert.Error(t, err)
 
 	// Read from existing env
 	Server.Accounts.ProofOfWork = 0
-	os.Setenv("BITMAELUM_SERVER_CONFIG", "/etc/bitmaelum/server-config.yml")
+	_ = os.Setenv("BITMAELUM_SERVER_CONFIG", "/etc/bitmaelum/server-config.yml")
 	err = LoadServerConfigOrPass("")
 	assert.NoError(t, err)
 	assert.Equal(t, 22, Server.Accounts.ProofOfWork)
diff --git a/internal/container/container_test.go b/internal/container/container_test.go
index 729792e..9371687 100644
--- a/internal/container/container_test.go
+++ b/internal/container/container_test.go
@@ -35,7 +35,7 @@ func TestContainer(t *testing.T) {
 	c := NewContainer()
 
 	// We set a function that we can call here
-	c.Set("foo", ServiceTypeSingle, func() (interface{}, error) {
+	c.SetShared("foo", func() (interface{}, error) {
 		return func() string {
 			return "this is foo"
 		}, nil
@@ -45,7 +45,7 @@ func TestContainer(t *testing.T) {
 	assert.True(t, ok)
 	assert.Equal(t, "this is foo", svc())
 
-	c.Set("foo", ServiceTypeSingle, func() (interface{}, error) { called++; return barFunc, nil })
+	c.SetShared("foo", func() (interface{}, error) { called++; return barFunc, nil })
 	_, _ = c.Get("foo").(func() string)
 	assert.Equal(t, 1, called)
 	_, _ = c.Get("foo").(func() string)
@@ -55,7 +55,7 @@ func TestContainer(t *testing.T) {
 	_, _ = c.Get("foo").(func() string)
 	assert.Equal(t, 1, called)
 
-	c.Set("foo", ServiceTypeMulti, func() (interface{}, error) { called++; return barFunc, nil })
+	c.SetNonShared("foo", func() (interface{}, error) { called++; return barFunc, nil })
 	_, _ = c.Get("foo").(func() string)
 	assert.Equal(t, 2, called)
 	_, _ = c.Get("foo").(func() string)
diff --git a/internal/message/catalog_test.go b/internal/message/catalog_test.go
index faf14e6..470bfe9 100644
--- a/internal/message/catalog_test.go
+++ b/internal/message/catalog_test.go
@@ -28,10 +28,7 @@ import (
 	"testing"
 	"time"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
-	bmtest "github.com/bitmaelum/bitmaelum-suite/internal/testing"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
 )
@@ -41,7 +38,7 @@ func TestCatalogNewCatalog(t *testing.T) {
 
 	assert.Equal(t, "john!", c.From.Address)
 	assert.False(t, c.CreatedAt.Before(time.Now().Add(-1*time.Second)))
-	assert.Equal(t, "", c.Subject)
+	assert.Equal(t, "subject", c.Subject)
 }
 
 func TestCatalogAddFlags(t *testing.T) {
@@ -77,13 +74,11 @@ func TestCatalogAddLabels(t *testing.T) {
 func TestCatalogSetToAddress(t *testing.T) {
 	c := genCatalog()
 
-	assert.Empty(t, c.To)
-
-	addr, err := address.NewAddress("jane!")
+	addr, err := address.NewAddress("joe!")
 	assert.NoError(t, err)
-	c.SetToAddress(*addr, "Jane Doe")
-	assert.Equal(t, "jane!", c.To.Address)
-	assert.Equal(t, "Jane Doe", c.To.Name)
+	c.SetToAddress(*addr, "Joe Doe")
+	assert.Equal(t, "joe!", c.To.Address)
+	assert.Equal(t, "Joe Doe", c.To.Name)
 }
 
 func TestCatalogAddAttachment(t *testing.T) {
@@ -140,16 +135,6 @@ func TestCatalogAddAttachment(t *testing.T) {
 func TestCatalogAddBlock(t *testing.T) {
 	c := genCatalog()
 
-	// // Setup afero
-	// fs = afero.NewMemMapFs()
-	//
-	// // 1 px gif
-	// buf1, err := base64.StdEncoding.DecodeString("R0lGODlhAQABAAAAACH5BAEAAAAALAAAAAABAAEAAAI=")
-	// assert.NoError(t, err)
-	// _ = afero.WriteFile(fs, "/dir/image.png", buf1, 0644)
-	//
-	// _ = afero.WriteFile(fs, "/dir/largerfile.dat", buf2, 0644)
-
 	// Nothing attached
 	assert.Len(t, c.Blocks, 0)
 
@@ -208,17 +193,8 @@ func TestCatalogAddBlock(t *testing.T) {
 }
 
 func genCatalog() *Catalog {
-	privKey, pubKey, _ := bmtest.ReadTestKey("../../testdata/key-1.json")
-
-	info := internal.AccountInfo{
-		Default:   true,
-		Address:   "john!",
-		Name:      "John Doe",
-		Settings:  nil,
-		PrivKey:   *privKey,
-		PubKey:    *pubKey,
-		Pow:       &proofofwork.ProofOfWork{},
-		RoutingID: "",
-	}
-	return NewCatalog(&info)
+	addrFrom, _ := address.NewAddress("john!")
+	addrTo, _ := address.NewAddress("jane!")
+
+	return NewCatalog(addrFrom, addrTo, "subject")
 }
diff --git a/internal/compress/compress_test.go b/internal/message/compress_test.go
similarity index 99%
rename from internal/compress/compress_test.go
rename to internal/message/compress_test.go
index d287cea..dbb8055 100644
--- a/internal/compress/compress_test.go
+++ b/internal/message/compress_test.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package compress
+package message
 
 import (
 	"bytes"
diff --git a/pkg/bmcrypto/encrypt_test.go b/internal/message/encrypt_test.go
similarity index 82%
rename from pkg/bmcrypto/encrypt_test.go
rename to internal/message/encrypt_test.go
index fbe742b..fc857de 100644
--- a/pkg/bmcrypto/encrypt_test.go
+++ b/internal/message/encrypt_test.go
@@ -17,22 +17,23 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package bmcrypto
+package message
 
 import (
 	"io/ioutil"
 	"testing"
 
+	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/stretchr/testify/assert"
 )
 
 func TestEncrypt(t *testing.T) {
 	// RSA Encryption
 	data, _ := ioutil.ReadFile("../../testdata/pubkey.rsa")
-	pubKey, _ := NewPubKey(string(data))
+	pubKey, _ := bmcrypto.NewPubKey(string(data))
 
 	data, _ = ioutil.ReadFile("../../testdata/privkey.rsa")
-	privKey, _ := NewPrivKey(string(data))
+	privKey, _ := bmcrypto.NewPrivKey(string(data))
 
 	cipher, _, _, err := Encrypt(*pubKey, []byte("foobar"))
 	assert.Nil(t, err)
@@ -43,8 +44,8 @@ func TestEncrypt(t *testing.T) {
 	assert.Equal(t, []byte("foobar"), plaintext)
 
 	// ED25519 Dual Key-Exchange + Encryption
-	priv25519Key, _ := NewPrivKey("ed25519 MC4CAQAwBQYDK2VwBCIEIBJsN8lECIdeMHEOZhrdDNEZl5BuULetZsbbdsZBjZ8a")
-	pub25519Key, _ := NewPubKey("ed25519 MCowBQYDK2VwAyEAblFzZuzz1vItSqdHbr/3DZMYvdoy17ALrjq3BM7kyKE=")
+	priv25519Key, _ := bmcrypto.NewPrivKey("ed25519 MC4CAQAwBQYDK2VwBCIEIBJsN8lECIdeMHEOZhrdDNEZl5BuULetZsbbdsZBjZ8a")
+	pub25519Key, _ := bmcrypto.NewPubKey("ed25519 MCowBQYDK2VwAyEAblFzZuzz1vItSqdHbr/3DZMYvdoy17ALrjq3BM7kyKE=")
 	cipher, txID, _, err := Encrypt(*pub25519Key, []byte("foobar"))
 	assert.Nil(t, err)
 	assert.NotEqual(t, []byte("foobar"), cipher)
@@ -55,7 +56,7 @@ func TestEncrypt(t *testing.T) {
 }
 
 func TestErrors(t *testing.T) {
-	priv, pub, err := generateKeyPairECDSA()
+	priv, pub, err := bmcrypto.GenerateKeyPair(bmcrypto.KeyTypeECDSA)
 	assert.NoError(t, err)
 
 	cipher, txID, _, err := Encrypt(*pub, []byte("foobar"))
diff --git a/internal/message/signature_test.go b/internal/message/signature_test.go
new file mode 100644
index 0000000..f1300b9
--- /dev/null
+++ b/internal/message/signature_test.go
@@ -0,0 +1,248 @@
+// Copyright (c) 2020 BitMaelum Authors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+// the Software, and to permit persons to whom the Software is furnished to do so,
+// subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+package message
+
+import (
+	"testing"
+
+	"github.com/bitmaelum/bitmaelum-suite/internal/config"
+	"github.com/bitmaelum/bitmaelum-suite/internal/container"
+	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
+	testing2 "github.com/bitmaelum/bitmaelum-suite/internal/testing"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
+	"github.com/stretchr/testify/assert"
+)
+
+const (
+	expectedServerSignature = "smq7qWrkwTL1vquhEAn/WoRDZBT1BjUQaUSsmTSSPePLRpM1sjX10mJwxXlivYOIlgTtJ+0SIeBM9rDPBSTw5JJhOS9ZFmpAYEPG9tkU+9EjxfBNnEBPrsYaqE81tO7OtY4xFrlYecdhepGUQSxbZQU4+Ih5jE9jLb+SuUxR6lGw2u2P+Ngy75dD33zlMTTgnmaVTxBueRlfArDExW5QE+pFv9/uFi8xM5a7eGnQHSjufQ9gM6WOWzhyWAnKI+6XMwx3MoQ53H3OU2vn4tPSUQQyxB+L4WTH9JtC0nLC0ggzvo5LOdCw4rCljsciYiEZ2WssGD9kXLGFIU/ixEX2Kw=="
+	expectedClientSignature = "lIqI1QYBRHl7yRW367Lx2n/PFadrYDZ2a2NGSaL40EKum0ncOIXs8CIqKZ+LCUgmK2a9iH2d3mbXVPwZ3PBGsVgReaomyG6NrDbZ0PCbgnjmrmkVAFV0bDHlOxUl/BzyV+seIL7FL0lu+cODaHkmzH16FsZ5Vqcf1/Qe2GR/0Ka6xbWcIcajGsKtTx+WtGeZGZ5oLbAFatEjiv5gMAn2umKpP+w7uKhPa6CsYkv2YMVw+z/1NU2CO0jE6/2muihF9x4nPw6yiy+sXP86B26FQXLBcMgTZ4TAtzr/b2KvcEDj8y8HISs/YHJvTdqAXzYTPnha37ZIIZ7ce27Z41GAUQ=="
+)
+
+func TestSignHeader(t *testing.T) {
+	setupServer()
+
+	header := &Header{}
+	_ = testing2.ReadJSON("../../testdata/header-002.json", &header)
+	assert.Empty(t, header.Signatures.Server)
+	err := SignServerHeader(header)
+	assert.NoError(t, err)
+
+	assert.Equal(t, expectedServerSignature, header.Signatures.Server)
+
+	// Already present, don't overwrite
+	_ = testing2.ReadJSON("../../testdata/header-002.json", &header)
+	assert.NotEmpty(t, header.Signatures.Server)
+	header.Signatures.Server = "foobar"
+	err = SignServerHeader(header)
+	assert.NoError(t, err)
+
+	assert.Equal(t, "foobar", header.Signatures.Server)
+}
+
+func TestVerifyHeader(t *testing.T) {
+	setupServer()
+
+	header := &Header{}
+	_ = testing2.ReadJSON("../../testdata/header-002.json", &header)
+	assert.Empty(t, header.Signatures.Server)
+	err := SignServerHeader(header)
+	assert.NoError(t, err)
+	assert.Equal(t, expectedServerSignature, header.Signatures.Server)
+
+	// All is ok
+	ok := VerifyServerHeader(*header)
+	assert.True(t, ok)
+
+	// Incorrect decoding
+	header.Signatures.Server = "A"
+	ok = VerifyServerHeader(*header)
+	assert.False(t, ok)
+
+	// Empty sig is not ok
+	header.Signatures.Server = ""
+	ok = VerifyServerHeader(*header)
+	assert.False(t, ok)
+
+	// incorrect key
+	header.Signatures.Server = "Zm9vYmFy"
+	ok = VerifyServerHeader(*header)
+	assert.False(t, ok)
+}
+
+func setupServer() {
+	addr, _ := address.NewAddress("foobar!")
+
+	// Note: our mail server uses key1
+	privKey, pubKey, err := testing2.ReadTestKey("../../testdata/key-1.json")
+	if err != nil {
+		panic(err)
+	}
+	config.Routing = config.RoutingConfig{
+		RoutingID:  "12345678",
+		PrivateKey: *privKey,
+		PublicKey:  *pubKey,
+	}
+
+	// Setup container with mock repository for routing
+	repo, _ := resolver.NewMockRepository()
+	container.Instance.SetShared("resolver", func() (interface{}, error) {
+		return resolver.KeyRetrievalService(repo), nil
+	})
+
+	uploadAddress(repo, *addr, "111100000000000000000000000097026f0daeaec1aeb8351b096637679cf350", "87654321", "../../testdata/key-2.json")
+	uploadAddress(repo, *addr, "111100000000000000018f66a0f3591a883f2b9cc3e95a497e7cf9da1071b4cc", "12345678", "../../testdata/key-3.json")
+
+	// Note: our mail server uses key1
+	privKey, pubKey, err = testing2.ReadTestKey("../../testdata/key-1.json")
+	if err != nil {
+		panic(err)
+	}
+	ri := resolver.RoutingInfo{
+		Hash:      "12345678",
+		PublicKey: *pubKey,
+		Routing:   "127.0.0.1",
+	}
+	_ = repo.UploadRouting(&ri, *privKey)
+}
+
+func uploadAddress(repo resolver.AddressRepository, addr address.Address, addrHash string, routingId string, keyPath string) {
+	pow := proofofwork.NewWithoutProof(1, "foobar")
+
+	privKey, pubKey, err := testing2.ReadTestKey(keyPath)
+	if err != nil {
+		panic(err)
+	}
+
+	ai := resolver.AddressInfo{
+		Hash:        addrHash,
+		PublicKey:   *pubKey,
+		RoutingID:   routingId,
+		Pow:         pow.String(),
+		RoutingInfo: resolver.RoutingInfo{},
+	}
+	_ = repo.UploadAddress(addr, &ai, *privKey, *pow, "")
+
+}
+
+func TestSignClientHeader(t *testing.T) {
+	privKey := setupClient()
+
+	header := &Header{}
+	_ = testing2.ReadJSON("../../testdata/header-001.json", &header)
+	assert.Empty(t, header.Signatures.Client)
+	err := SignClientHeader(header, *privKey)
+	assert.NoError(t, err)
+	assert.Equal(t, expectedClientSignature, header.Signatures.Client)
+
+	// Already present, don't overwrite
+	_ = testing2.ReadJSON("../../testdata/header-001.json", &header)
+	assert.NotEmpty(t, header.Signatures.Client)
+	header.Signatures.Client = "foobar"
+	err = SignClientHeader(header, *privKey)
+	assert.NoError(t, err)
+
+	assert.Equal(t, "foobar", header.Signatures.Client)
+}
+
+func TestVerifyClientHeader(t *testing.T) {
+	privKey := setupClient()
+
+	header := &Header{}
+	_ = testing2.ReadJSON("../../testdata/header-001.json", &header)
+	assert.Empty(t, header.Signatures.Client)
+	err := SignClientHeader(header, *privKey)
+	assert.NoError(t, err)
+	assert.Equal(t, expectedClientSignature, header.Signatures.Client)
+
+	// All is ok
+	ok := VerifyClientHeader(*header)
+	assert.True(t, ok)
+
+	// Incorrect decoding
+	header.Signatures.Client = "A"
+	ok = VerifyClientHeader(*header)
+	assert.False(t, ok)
+
+	// Empty sig is not ok
+	header.Signatures.Client = ""
+	ok = VerifyClientHeader(*header)
+	assert.False(t, ok)
+
+	// incorrect key
+	header.Signatures.Client = "Zm9vYmFy"
+	ok = VerifyClientHeader(*header)
+	assert.False(t, ok)
+}
+
+func setupClient() *bmcrypto.PrivKey {
+	addr, _ := address.NewAddress("foobar!")
+
+	// Setup container with mock repository for routing
+	repo, _ := resolver.NewMockRepository()
+	container.Instance.SetShared("resolver", func() (interface{}, error) {
+		return resolver.KeyRetrievalService(repo), nil
+	})
+
+	pow := proofofwork.NewWithoutProof(1, "foobar")
+	var (
+		ai resolver.AddressInfo
+		ri resolver.RoutingInfo
+	)
+
+	privKey, pubKey, err := testing2.ReadTestKey("../../testdata/key-1.json")
+	if err != nil {
+		panic(err)
+	}
+	ai = resolver.AddressInfo{
+		RoutingID:   "87654321",
+		PublicKey:   *pubKey,
+		RoutingInfo: resolver.RoutingInfo{},
+		Pow:         pow.String(),
+		Hash:        "000000000000000000000000000097026f0daeaec1aeb8351b096637679cf350",
+	}
+	_ = repo.UploadAddress(*addr, &ai, *privKey, *pow, "")
+
+	ri = resolver.RoutingInfo{
+		PublicKey: *pubKey,
+		Routing:   "127.0.0.1",
+		Hash:      "12345678",
+	}
+
+	_ = repo.UploadRouting(&ri, *privKey)
+
+	// Note: our sender uses key3
+	privKey, pubKey, err = testing2.ReadTestKey("../../testdata/key-3.json")
+	if err != nil {
+		panic(err)
+	}
+
+	ai = resolver.AddressInfo{
+		RoutingID:   "12345678",
+		RoutingInfo: resolver.RoutingInfo{},
+		PublicKey:   *pubKey,
+		Hash:        "000000000000000000018f66a0f3591a883f2b9cc3e95a497e7cf9da1071b4cc",
+		Pow:         pow.String(),
+	}
+	_ = repo.UploadAddress(*addr, &ai, *privKey, *pow, "")
+
+	return privKey
+}
diff --git a/internal/parse/parse_args_test.go b/internal/parse_args_test.go
similarity index 82%
rename from internal/parse/parse_args_test.go
rename to internal/parse_args_test.go
index a4fbd6d..631d872 100644
--- a/internal/parse/parse_args_test.go
+++ b/internal/parse_args_test.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package parse
+package internal
 
 import (
 	"testing"
@@ -29,25 +29,25 @@ import (
 func TestPermissions(t *testing.T) {
 	var err error
 
-	err = ManagementPermissions([]string{})
+	err = CheckManagementPermissions([]string{})
 	assert.NoError(t, err)
 
-	err = ManagementPermissions([]string{"flush"})
+	err = CheckManagementPermissions([]string{"flush"})
 	assert.NoError(t, err)
 
-	err = ManagementPermissions([]string{"flush", "invite"})
+	err = CheckManagementPermissions([]string{"flush", "invite"})
 	assert.NoError(t, err)
 
-	err = ManagementPermissions([]string{"foo"})
+	err = CheckManagementPermissions([]string{"foo"})
 	assert.Error(t, err)
 
-	err = ManagementPermissions([]string{"flush", "foo"})
+	err = CheckManagementPermissions([]string{"flush", "foo"})
 	assert.Error(t, err)
 
-	err = AccountPermissions([]string{"get-headers"})
+	err = CheckAccountPermissions([]string{"get-headers"})
 	assert.NoError(t, err)
 
-	err = AccountPermissions([]string{"flush"})
+	err = CheckAccountPermissions([]string{"flush"})
 	assert.Error(t, err)
 }
 
diff --git a/internal/account_test.go b/internal/routing.go
similarity index 63%
rename from internal/account_test.go
rename to internal/routing.go
index 04f9333..3d0c29e 100644
--- a/internal/account_test.go
+++ b/internal/routing.go
@@ -20,34 +20,15 @@
 package internal
 
 import (
-	"testing"
-
 	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
-	"github.com/stretchr/testify/assert"
 )
 
-func TestInfoToOrg(t *testing.T) {
-	info := &OrganisationInfo{
-		Addr:        "foo",
-		FullName:    "bar",
-		PrivKey:     bmcrypto.PrivKey{},
-		PubKey:      bmcrypto.PubKey{},
-		Pow:         proofofwork.New(22, "foobar", 1234),
-		Validations: nil,
-	}
-
-	org, err := InfoToOrg(*info)
-	assert.NoError(t, err)
-	assert.Equal(t, "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", org.Hash.String())
-}
-
-func TestAccountInfoAddressHash(t *testing.T) {
-	info := &AccountInfo{
-		Default: false,
-		Address: "example!",
-		Name:    "John DOe",
-	}
-
-	assert.Equal(t, "2244643da7475120bf84d744435d15ea297c36ca165ea0baaa69ec818d0e952f", info.AddressHash().String())
+// RoutingInfo represents a routing configuration for a server
+type RoutingInfo struct {
+	RoutingID string                   `json:"routing_id"`    // ID
+	PrivKey   bmcrypto.PrivKey         `json:"priv_key"`      // PEM encoded private key
+	PubKey    bmcrypto.PubKey          `json:"pub_key"`       // PEM encoded public key
+	Pow       *proofofwork.ProofOfWork `json:"pow,omitempty"` // Proof of work
+	Route     string                   `json:"route"`         // Route to server
 }
diff --git a/internal/server/signature_test.go b/internal/server/signature_test.go
deleted file mode 100644
index 43f484e..0000000
--- a/internal/server/signature_test.go
+++ /dev/null
@@ -1,153 +0,0 @@
-// Copyright (c) 2020 BitMaelum Authors
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-// the Software, and to permit persons to whom the Software is furnished to do so,
-// subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-package server
-
-import (
-	"testing"
-
-	"github.com/bitmaelum/bitmaelum-suite/internal/config"
-	"github.com/bitmaelum/bitmaelum-suite/internal/container"
-	"github.com/bitmaelum/bitmaelum-suite/internal/message"
-	"github.com/bitmaelum/bitmaelum-suite/internal/resolver"
-	bmtest "github.com/bitmaelum/bitmaelum-suite/internal/testing"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
-	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
-	"github.com/stretchr/testify/assert"
-)
-
-const (
-	expectedSignature = "smq7qWrkwTL1vquhEAn/WoRDZBT1BjUQaUSsmTSSPePLRpM1sjX10mJwxXlivYOIlgTtJ+0SIeBM9rDPBSTw5JJhOS9ZFmpAYEPG9tkU+9EjxfBNnEBPrsYaqE81tO7OtY4xFrlYecdhepGUQSxbZQU4+Ih5jE9jLb+SuUxR6lGw2u2P+Ngy75dD33zlMTTgnmaVTxBueRlfArDExW5QE+pFv9/uFi8xM5a7eGnQHSjufQ9gM6WOWzhyWAnKI+6XMwx3MoQ53H3OU2vn4tPSUQQyxB+L4WTH9JtC0nLC0ggzvo5LOdCw4rCljsciYiEZ2WssGD9kXLGFIU/ixEX2Kw=="
-)
-
-func TestSignHeader(t *testing.T) {
-	setup()
-
-	header := &message.Header{}
-	_ = bmtest.ReadJSON("../../testdata/header-002.json", &header)
-	assert.Empty(t, header.Signatures.Server)
-	err := SignHeader(header)
-	assert.NoError(t, err)
-
-	assert.Equal(t, expectedSignature, header.Signatures.Server)
-
-	// Already present, don't overwrite
-	_ = bmtest.ReadJSON("../../testdata/header-002.json", &header)
-	assert.NotEmpty(t, header.Signatures.Server)
-	header.Signatures.Server = "foobar"
-	err = SignHeader(header)
-	assert.NoError(t, err)
-
-	assert.Equal(t, "foobar", header.Signatures.Server)
-}
-
-func TestVerifyHeader(t *testing.T) {
-	setup()
-
-	header := &message.Header{}
-	_ = bmtest.ReadJSON("../../testdata/header-002.json", &header)
-	assert.Empty(t, header.Signatures.Server)
-	err := SignHeader(header)
-	assert.NoError(t, err)
-	assert.Equal(t, expectedSignature, header.Signatures.Server)
-
-	// All is ok
-	ok := VerifyHeader(*header)
-	assert.True(t, ok)
-
-	// Incorrect decoding
-	header.Signatures.Server = "A"
-	ok = VerifyHeader(*header)
-	assert.False(t, ok)
-
-	// Empty sig is not ok
-	header.Signatures.Server = ""
-	ok = VerifyHeader(*header)
-	assert.False(t, ok)
-
-	// incorrect key
-	header.Signatures.Server = "Zm9vYmFy"
-	ok = VerifyHeader(*header)
-	assert.False(t, ok)
-}
-
-func setup() {
-	addr, _ := address.NewAddress("foobar!")
-
-	// Note: our mail server uses key1
-	privKey, pubKey, err := bmtest.ReadTestKey("../../testdata/key-1.json")
-	if err != nil {
-		panic(err)
-	}
-	config.Routing = config.RoutingConfig{
-		RoutingID:  "12345678",
-		PrivateKey: *privKey,
-		PublicKey:  *pubKey,
-	}
-
-	// Setup container with mock repository for routing
-	repo, _ := resolver.NewMockRepository()
-	container.Set("resolver", func() (interface{}, error) {
-		return resolver.KeyRetrievalService(repo), nil
-	})
-
-	pow := proofofwork.NewWithoutProof(1, "foobar")
-	var (
-		ai resolver.AddressInfo
-		ri resolver.RoutingInfo
-	)
-
-	privKey, pubKey, err = bmtest.ReadTestKey("../../testdata/key-2.json")
-	if err != nil {
-		panic(err)
-	}
-	ai = resolver.AddressInfo{
-		Hash:        "111100000000000000000000000097026f0daeaec1aeb8351b096637679cf350",
-		PublicKey:   *pubKey,
-		RoutingID:   "87654321",
-		Pow:         pow.String(),
-		RoutingInfo: resolver.RoutingInfo{},
-	}
-	_ = repo.UploadAddress(*addr, &ai, *privKey, *pow, "")
-
-	privKey, pubKey, err = bmtest.ReadTestKey("../../testdata/key-3.json")
-	if err != nil {
-		panic(err)
-	}
-	ai = resolver.AddressInfo{
-		Hash:        "111100000000000000018f66a0f3591a883f2b9cc3e95a497e7cf9da1071b4cc",
-		PublicKey:   *pubKey,
-		RoutingID:   "12345678",
-		Pow:         pow.String(),
-		RoutingInfo: resolver.RoutingInfo{},
-	}
-	_ = repo.UploadAddress(*addr, &ai, *privKey, *pow, "")
-
-	// Note: our mail server uses key1
-	privKey, pubKey, err = bmtest.ReadTestKey("../../testdata/key-1.json")
-	if err != nil {
-		panic(err)
-	}
-	ri = resolver.RoutingInfo{
-		Hash:      "12345678",
-		PublicKey: *pubKey,
-		Routing:   "127.0.0.1",
-	}
-	_ = repo.UploadRouting(&ri, *privKey)
-}
diff --git a/internal/invite/signature_test.go b/internal/signature/invite_test.go
similarity index 99%
rename from internal/invite/signature_test.go
rename to internal/signature/invite_test.go
index 24bf160..e5bf914 100644
--- a/internal/invite/signature_test.go
+++ b/internal/signature/invite_test.go
@@ -17,7 +17,7 @@
 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-package invite
+package signature
 
 import (
 	"testing"
diff --git a/internal/vault/account_test.go b/internal/vault/account_test.go
index af3c21f..9e50b6c 100644
--- a/internal/vault/account_test.go
+++ b/internal/vault/account_test.go
@@ -22,8 +22,9 @@ package vault
 import (
 	"testing"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/bmcrypto"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -33,13 +34,14 @@ func TestVaultAccount(t *testing.T) {
 
 	assert.Len(t, v.Store.Accounts, 0)
 
-	acc := internal.AccountInfo{
-		Address: "example!",
+	addr, _ := address.NewAddress("example!")
+	acc := AccountInfo{
+		Address: *addr,
 		Name:    "Example Account",
 	}
 	v.AddAccount(acc)
 	assert.Len(t, v.Store.Accounts, 1)
-	assert.Equal(t, "example!", v.Store.Accounts[0].Address)
+	assert.Equal(t, "example!", v.Store.Accounts[0].Address.String())
 
 	a, _ := address.NewAddress("example!")
 	assert.True(t, v.HasAccount(*a))
@@ -50,7 +52,7 @@ func TestVaultAccount(t *testing.T) {
 	a, _ = address.NewAddress("example!")
 	o, err := v.GetAccountInfo(*a)
 	assert.NoError(t, err)
-	assert.Equal(t, "example!", o.Address)
+	assert.Equal(t, "example!", o.Address.String())
 
 	a, _ = address.NewAddress("notexist!")
 	o, err = v.GetAccountInfo(*a)
@@ -73,34 +75,61 @@ func TestVaultGetDefaultAccount(t *testing.T) {
 
 	assert.Len(t, v.Store.Accounts, 0)
 
-	acc := internal.AccountInfo{
+	addr, _ := address.NewAddress("acc1!")
+	acc := AccountInfo{
 		Default: false,
-		Address: "acc1!",
+		Address: *addr,
 	}
 	v.AddAccount(acc)
 
-	acc = internal.AccountInfo{
+	addr, _ = address.NewAddress("acc2!")
+	acc = AccountInfo{
 		Default: true,
-		Address: "acc2!",
+		Address: *addr,
 	}
 	v.AddAccount(acc)
 
-	acc = internal.AccountInfo{
+	addr, _ = address.NewAddress("acc3!")
+	acc = AccountInfo{
 		Default: false,
-		Address: "acc3!",
+		Address: *addr,
 	}
 	v.AddAccount(acc)
 	assert.Len(t, v.Store.Accounts, 3)
 
 	da := v.GetDefaultAccount()
-	assert.Equal(t, "acc2!", da.Address)
+	assert.Equal(t, "acc2!", da.Address.String())
 
 	// Without default set, pick the first
 	v.Store.Accounts[1].Default = false
 	da = v.GetDefaultAccount()
-	assert.Equal(t, "acc1!", da.Address)
+	assert.Equal(t, "acc1!", da.Address.String())
 
 	v, _ = New("", []byte{})
 	da = v.GetDefaultAccount()
 	assert.Nil(t, da)
 }
+
+func TestInfoToOrg(t *testing.T) {
+	info := &OrganisationInfo{
+		Addr:        "foo",
+		FullName:    "bar",
+		PrivKey:     bmcrypto.PrivKey{},
+		PubKey:      bmcrypto.PubKey{},
+		Pow:         proofofwork.New(22, "foobar", 1234),
+		Validations: nil,
+	}
+
+	assert.Equal(t, "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", info.ToOrg().Hash.String())
+}
+
+func TestAccountInfoAddressHash(t *testing.T) {
+	addr, _ := address.NewAddress("example!")
+	info := &AccountInfo{
+		Default: false,
+		Address: *addr,
+		Name:    "John DOe",
+	}
+
+	assert.Equal(t, "2244643da7475120bf84d744435d15ea297c36ca165ea0baaa69ec818d0e952f", info.Address.Hash().String())
+}
diff --git a/internal/vault/organisation_test.go b/internal/vault/organisation_test.go
index 637930b..66ad62f 100644
--- a/internal/vault/organisation_test.go
+++ b/internal/vault/organisation_test.go
@@ -22,7 +22,6 @@ package vault
 import (
 	"testing"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/hash"
 	"github.com/stretchr/testify/assert"
 )
@@ -33,7 +32,7 @@ func TestVaultAddOrganisation(t *testing.T) {
 
 	assert.Len(t, v.Store.Organisations, 0)
 
-	org := internal.OrganisationInfo{
+	org := OrganisationInfo{
 		Addr:     "example",
 		FullName: "Example Org",
 	}
diff --git a/internal/vault/vault_test.go b/internal/vault/vault_test.go
index af8a8c8..4844a16 100644
--- a/internal/vault/vault_test.go
+++ b/internal/vault/vault_test.go
@@ -22,8 +22,8 @@ package vault
 import (
 	"testing"
 
-	"github.com/bitmaelum/bitmaelum-suite/internal"
-	bmtest "github.com/bitmaelum/bitmaelum-suite/internal/testing"
+	testing2 "github.com/bitmaelum/bitmaelum-suite/internal/testing"
+	"github.com/bitmaelum/bitmaelum-suite/pkg/address"
 	"github.com/bitmaelum/bitmaelum-suite/pkg/proofofwork"
 	"github.com/spf13/afero"
 	"github.com/stretchr/testify/assert"
@@ -53,11 +53,12 @@ func TestNew(t *testing.T) {
 	err = v.WriteToDisk()
 	assert.Errorf(t, err, "vault seems to have invalid data. Refusing to overwrite the current vault")
 
-	privKey, pubKey, _ := bmtest.ReadTestKey("../../testdata/key-1.json")
+	privKey, pubKey, _ := testing2.ReadTestKey("../../testdata/key-1.json")
 
-	acc := &internal.AccountInfo{
+	addr, _ := address.NewAddress("foobar!")
+	acc := &AccountInfo{
 		Default:   false,
-		Address:   "foobar!",
+		Address:   *addr,
 		Name:      "Foo Bar",
 		Settings:  nil,
 		PrivKey:   *privKey,
@@ -87,17 +88,18 @@ func TestNew(t *testing.T) {
 }
 
 func TestFindShortRoutingId(t *testing.T) {
-	var acc internal.AccountInfo
+	var acc AccountInfo
 
 	v, _ := New("", []byte{})
 
-	acc = internal.AccountInfo{Address: "example!", RoutingID: "123456780000"}
+	addr, _ := address.NewAddress("example!")
+	acc = AccountInfo{Address: *addr, RoutingID: "123456780000"}
 	v.AddAccount(acc)
-	acc = internal.AccountInfo{Address: "example!", RoutingID: "123456780001"}
+	acc = AccountInfo{Address: *addr, RoutingID: "123456780001"}
 	v.AddAccount(acc)
-	acc = internal.AccountInfo{Address: "example!", RoutingID: "123456780002"}
+	acc = AccountInfo{Address: *addr, RoutingID: "123456780002"}
 	v.AddAccount(acc)
-	acc = internal.AccountInfo{Address: "example!", RoutingID: "154353535335"}
+	acc = AccountInfo{Address: *addr, RoutingID: "154353535335"}
 	v.AddAccount(acc)
 
 	assert.Equal(t, "154353535335", v.FindShortRoutingID("154"))
@@ -115,21 +117,25 @@ func TestVaultChangePassword(t *testing.T) {
 }
 
 func TestGetAccountOrDefault(t *testing.T) {
-	var acc *internal.AccountInfo
+	var acc *AccountInfo
 
 	v, _ := New("", []byte{})
-	acc = &internal.AccountInfo{Address: "example1!", RoutingID: "123456780000"}
+	addr, _ := address.NewAddress("example1!")
+	acc = &AccountInfo{Address: *addr, RoutingID: "123456780000"}
 	v.AddAccount(*acc)
-	acc = &internal.AccountInfo{Address: "example2!", RoutingID: "123456780001"}
+	addr, _ = address.NewAddress("example2!")
+	acc = &AccountInfo{Address: *addr, RoutingID: "123456780001"}
 	v.AddAccount(*acc)
-	acc = &internal.AccountInfo{Address: "example3!", RoutingID: "123456780002", Default: true}
+	addr, _ = address.NewAddress("example3!")
+	acc = &AccountInfo{Address: *addr, RoutingID: "123456780002", Default: true}
 	v.AddAccount(*acc)
-	acc = &internal.AccountInfo{Address: "example4!", RoutingID: "154353535335"}
+	addr, _ = address.NewAddress("example4!")
+	acc = &AccountInfo{Address: *addr, RoutingID: "154353535335"}
 	v.AddAccount(*acc)
 
 	acc = GetAccountOrDefault(v, "")
-	assert.Equal(t, "example3!", acc.Address)
+	assert.Equal(t, "example3!", acc.Address.String())
 
 	acc = GetAccountOrDefault(v, "example2!")
-	assert.Equal(t, "example2!", acc.Address)
+	assert.Equal(t, "example2!", acc.Address.String())
 }
diff --git a/pkg/bmcrypto/generate_test.go b/pkg/bmcrypto/generate_test.go
index aad3955..029607a 100644
--- a/pkg/bmcrypto/generate_test.go
+++ b/pkg/bmcrypto/generate_test.go
@@ -37,7 +37,7 @@ func TestGenerate(t *testing.T) {
 	assert.Nil(t, err)
 	assert.IsType(t, (*rsa.PrivateKey)(nil), privKey.K)
 	assert.IsType(t, (*rsa.PublicKey)(nil), pubKey.K)
-	assert.Equal(t, pubKey.K.(*rsa.PublicKey).Size() * 8, RsaBits[0])
+	assert.Equal(t, pubKey.K.(*rsa.PublicKey).Size()*8, RsaBits[0])
 
 	// Check if we can verify with this key
 	sig, err := Sign(*privKey, message)
@@ -80,6 +80,6 @@ func TestRSAV1(t *testing.T) {
 	assert.Nil(t, err)
 	assert.IsType(t, (*rsa.PrivateKey)(nil), privKey.K)
 	assert.IsType(t, (*rsa.PublicKey)(nil), pubKey.K)
-	assert.Equal(t, pubKey.K.(*rsa.PublicKey).Size() * 8, RsaBits[1])
+	assert.Equal(t, pubKey.K.(*rsa.PublicKey).Size()*8, RsaBits[1])
 
 }