🏡 index : ~doyle/chartered.git

<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js light">
    <head>
        <!-- Book generated using mdBook -->
        <meta charset="UTF-8">
        <title>Chartered</title>
        <meta name="robots" content="noindex" />
        <!-- Custom HTML head -->
        <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="theme-color" content="#ffffff" />

        <link rel="icon" href="favicon.svg">
        <link rel="shortcut icon" href="favicon.png">
        <link rel="stylesheet" href="css/variables.css">
        <link rel="stylesheet" href="css/general.css">
        <link rel="stylesheet" href="css/chrome.css">
        <link rel="stylesheet" href="css/print.css" media="print">
        <!-- Fonts -->
        <link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
        <link rel="stylesheet" href="fonts/fonts.css">
        <!-- Highlight.js Stylesheets -->
        <link rel="stylesheet" href="highlight.css">
        <link rel="stylesheet" href="tomorrow-night.css">
        <link rel="stylesheet" href="ayu-highlight.css">

        <!-- Custom theme stylesheets -->
    </head>
    <body>
        <!-- Provide site root to javascript -->
        <script type="text/javascript">
            var path_to_root = "";
            var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
        </script>

        <!-- Work around some values being stored in localStorage wrapped in quotes -->
        <script type="text/javascript">
            try {
                var theme = localStorage.getItem('mdbook-theme');
                var sidebar = localStorage.getItem('mdbook-sidebar');

                if (theme.startsWith('"') && theme.endsWith('"')) {
                    localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
                }

                if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                    localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
                }
            } catch (e) { }
        </script>

        <!-- Set the theme before any content is loaded, prevents flash -->
        <script type="text/javascript">
            var theme;
            try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
            if (theme === null || theme === undefined) { theme = default_theme; }
            var html = document.querySelector('html');
            html.classList.remove('no-js')
            html.classList.remove('light')
            html.classList.add(theme);
            html.classList.add('js');
        </script>

        <!-- Hide / unhide sidebar before it is displayed -->
        <script type="text/javascript">
            var html = document.querySelector('html');
            var sidebar = 'hidden';
            if (document.body.clientWidth >= 1080) {
                try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
                sidebar = sidebar || 'visible';
            }
            html.classList.remove('sidebar-visible');
            html.classList.add("sidebar-" + sidebar);
        </script>

        <nav id="sidebar" class="sidebar" aria-label="Table of contents">
            <div class="sidebar-scrollbox">
                <ol class="chapter"><li class="chapter-item expanded affix "><a href="introduction.html">Introduction</a></li><li class="chapter-item expanded "><a href="getting-started/index.html"><strong aria-hidden="true">1.</strong> Getting Started</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="getting-started/installation.html"><strong aria-hidden="true">1.1.</strong> Server Installation</a></li><li class="chapter-item expanded "><a href="getting-started/user-guide.html"><strong aria-hidden="true">1.2.</strong> User Guide</a></li></ol></li><li class="chapter-item expanded "><a href="guide/index.html"><strong aria-hidden="true">2.</strong> Chartered Guide</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="guide/config-reference.html"><strong aria-hidden="true">2.1.</strong> Configuration Reference</a></li></ol></li></ol>
            </div>
            <div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
        </nav>

        <div id="page-wrapper" class="page-wrapper">

            <div class="page">
                <div id="menu-bar-hover-placeholder"></div>
                <div id="menu-bar" class="menu-bar sticky bordered">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                    </div>

                    <h1 class="menu-title">Chartered</h1>

                    <div class="right-buttons">
                        <a href="print.html" title="Print this book" aria-label="Print this book">
                            <i id="print-button" class="fa fa-print"></i>
                        </a>
                        <a href="https://github.com/w4/chartered/tree/main/book" title="Git repository" aria-label="Git repository">
                            <i id="git-repository-button" class="fa fa-github"></i>
                        </a>
                    </div>
                </div>

                <div id="search-wrapper" class="hidden">
                    <form id="searchbar-outer" class="searchbar-outer">
                        <input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                    </form>
                    <div id="searchresults-outer" class="searchresults-outer hidden">
                        <div id="searchresults-header" class="searchresults-header"></div>
                        <ul id="searchresults">
                        </ul>
                    </div>
                </div>
                <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
                <script type="text/javascript">
                    document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                    document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                    Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
                        link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                    });
                </script>

                <div id="content" class="content">
                    <main>
                        <h1 id="chartered-"><a class="header" href="#chartered-">chartered ✈️</a></h1>
<p>Chartered is an <a href="https://doc.rust-lang.org/cargo/reference/registries.html">alternative registry</a> implementation that goes a little
bit futher than your bog-standard registry, providing AAA (authentication,
authorisation &amp; accounting) guarentees for each of your crates.</p>
<h3 id="sections"><a class="header" href="#sections">Sections</a></h3>
<p><strong><a href="getting-started/index.html">Getting Started</a></strong></p>
<p>To get started with Chartered, running the servers &amp; uploading your first
crate.</p>
<p><strong><a href="guide/index.html">Chartered Guide</a></strong></p>
<p>The guide will give you the run down on all things chartered.</p>
<p><strong>Appendices:</strong></p>
<ul>
<li><a href="appendix/glossary.html">Glossary</a></li>
</ul>
<p><strong>Other documentation:</strong></p>
<ul>
<li><a href="https://github.com/w4/chartered/releases">Changelog</a></li>
<li><a href="https://doc.rust-lang.org/cargo/index.html">Cargo book</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="getting-started"><a class="header" href="#getting-started">Getting Started</a></h1>
<p>To get started with Chartered, run the server and upload your first crate.</p>
<ul>
<li><a href="getting-started/./installation.html">Server installation</a></li>
<li><a href="getting-started/./user-guide.html">User guide</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="server-installation"><a class="header" href="#server-installation">Server Installation</a></h1>
<p>Chartered's server comes in 3 parts:</p>
<ul>
<li><strong>chartered-git</strong>: hosts the git server which clients grab the crate index from, along with
their credentials to grab the crates from the next service,</li>
<li><strong>chartered-web</strong>: hosts the API portion of chartered, which serves the crates themselves
(or a redirect to them, depending on which storage backend you're using) and hosts the &quot;web
API&quot; which is consumed by our final service,</li>
<li><strong>chartered-frontend</strong>: a React-based <a href="https://crates.io/">crates.io</a>-like web UI for viewing
crates, managing organisations and viewing AAA data.</li>
</ul>
<p>Each of these services are hosted separately from one another, and could technically be swapped
out for other implementations - the only shared layer between the three of them is database
storage for crate lookups and authentication credential vending. All of the services have the
ability to be clustered with no extra configuration.</p>
<h3 id="backend-services"><a class="header" href="#backend-services">Backend Services</a></h3>
<p><code>chartered-git</code> and <code>chartered-web</code>'s setups are similar, first they need a database set up -
either SQLite or PostgreSQL, PostgreSQL is recommended anywhere outside of development/local
use for obvious reasons.</p>
<p>Both the aformentioned services have sane defaults for development and can be ran simply by
running the binary, the services will bind to <code>127.0.0.1:8899</code> and <code>127.0.0.1:8080</code> respectively
and store crate files in <code>/tmp/chartered</code>, configuration away from these defaults is simple.</p>
<p>Using the recommended setup, S3 &amp; PostgreSQL:</p>
<pre><code class="language-toml">bind_address = &quot;127.0.0.1:8080&quot; # hint: use a different port for each service
database_uri = &quot;postgres://user:password@localhost/chartered&quot;

# the below configuration options should only be set for chartered-web
storage_uri  = &quot;s3://s3-eu-west-1.amazonaws.com/my-cool-crate-store/&quot;
frontend_url = &quot;https://my.instance.chart.rs&quot; # this is used for CORS
                                              # if unset defaults to *

# openid connect provider
[auth.gitlab]
enabled = true
discovery_uri = &quot;https://gitlab.com/&quot;
client_id = &quot;[client-id]&quot;
client_secret = &quot;[client-secret]&quot;
</code></pre>
<p>Or, using the defaults of <code>chartered-web</code> as an example:</p>
<pre><code class="language-toml">bind_address = &quot;127.0.0.1:8899&quot;
database_uri = &quot;sqlite://chartered.db&quot;

storage_uri  = &quot;file:///tmp/chartered&quot;
</code></pre>
<p>These configuration files can be passed into each binary using the <code>-c</code> CLI argument.</p>
<p>Alternative crate stores will be considered, please consider contributing or
<a href="https://github.com/w4/chartered/issues">create an issue on GitHub</a>. <span style="color: transparent;">MySQL support, however, is a no-go.</span></p>
<p><code>chartered-web</code> &amp; <code>chartered-git</code> can be built from source easily or ran using the
Dockerfile:</p>
<pre><code>$ docker build https://github.com/w4/chartered.git#main \
    --target chartered-web \
    -t chartered-web:master
$ docker build https://github.com/w4/chartered.git#main \
    --target chartered-git \
    -t chartered-git:master
$ docker run -d chartered-web
$ docker run -d chartered-git
</code></pre>
<h3 id="frontend"><a class="header" href="#frontend">Frontend</a></h3>
<p>The frontend only needs to be configured to point to the <code>chartered-web</code> service. This can be
done by changing the bundled <code>config.json</code>. This can then be hosted in S3/minio/your preferred
static hosting platform, a Dockerfile can also be built which uses <a href="https://github.com/joseluisq/static-web-server"><code>static-web-server</code></a>
to run on your own server without another way of hosting static content:</p>
<pre><code class="language-sh"># buildkit doesn't yet support subdirectories for git repositories
$ DOCKER_BUILDKIT=0 docker build \
    https://github.com/w4/chartered.git#main:chartered-frontend \
    --build-arg BASE_URL=https://api.my.instance.chart.rs \
    -t chartered-frontend:master
$ docker run -d -p 8080:80 chartered-frontend:master
$ curl http://127.0.0.1:8080
&lt;!DOCTYPE html&gt;&lt;html lang=&quot;en&quot;&gt;&lt;head&gt;&lt;meta charset=&quot;UTF-8&quot;&gt;...
</code></pre>
<div style="break-before: page; page-break-before: always;"></div><h1 id="user-guide"><a class="header" href="#user-guide">User Guide</a></h1>
<p>Using chartered as a user is actually pretty simple, it's very much like your
standard Cargo registry except with two extra concepts: organisations and
permissions.</p>
<p>Organisations are very much like the organisations you'll likely have used on
your preferred SCM host, a group of users that have group-level permissions,
in Chartered case the permissions these users may have are:</p>
<h3 id="permissions"><a class="header" href="#permissions">Permissions</a></h3>
<ul>
<li><code>VISIBLE</code>
<ul>
<li>Essentially the base-level permission meaning the user belongs to the group,
if the user doesn't have this permission they're not in the group, this
permission at the crate-level means the user can download the crate and see
it in the WebUI.</li>
</ul>
</li>
<li><code>PUBLISH_VERSION</code>
<ul>
<li>Gives the ability to publish a new version for crates belonging to the group.</li>
</ul>
</li>
<li><code>YANK_VERSION</code>
<ul>
<li>Gives the ability to yank (and unyank) published versions for crates belonging
to the group.</li>
</ul>
</li>
<li><code>MANAGE_USERS</code>
<ul>
<li>Gives the ability to add (and remove) users from the group, and crates belonging
to the organisation.</li>
</ul>
</li>
<li><code>CREATE_CRATE</code>
<ul>
<li>Gives the ability to create a new crate under the organisation.</li>
</ul>
</li>
</ul>
<p>All these permissions, with the exception of <code>CREATE_CRATE</code>, can also be used at the
crate-level for giving extra permissions to org members for a particular crate - or
even users outside of the org. Bare in mind, however, these permissions are <em>additive</em> -
it is not possible for permissions to be <em>subtracted</em> from a user at the crate-level
if they have been granted them by the organisation.</p>
<h3 id="publishing-your-first-crate"><a class="header" href="#publishing-your-first-crate">Publishing your first crate</a></h3>
<p>With all this in mind, it's about time you started publishing your first crate!</p>
<p>Chartered has excellent integration with Cargo's <a href="https://doc.rust-lang.org/cargo/reference/registries.html">alternative registry</a>
implementation and is used very much like a standard alternative registry. The only
prerequisites for publishing your crate are:</p>
<ol>
<li>Have an SSH key added to your Chartered account, which you can do via the WebUI</li>
<li>Belong to an organisation you have the <code>PUBLISH_VERSION</code> permission for, anyone can
create a new organisation if you don't already belong to one.</li>
</ol>
<p>And once you're ready, you can add the organisation's registry to your <code>.cargo/config.toml</code>
like so:</p>
<pre><code class="language-toml">[registries]
my-organisation = { index = &quot;ssh://ssh.your.instance.of.chart.rs/my-organisation&quot; }
</code></pre>
<p>(You should create this file if it doesn't already exist)</p>
<p>You can now publish the crate using cargo as you normally would, except with the
registry specified:</p>
<pre><code class="language-sh">$ cargo publish --registry my-organisation --token &quot;&quot;
</code></pre>
<p>Note: the token is purposefully empty, as the token will be vended by the index based
on your SSH key.</p>
<h3 id="pulling-in-dependencies"><a class="header" href="#pulling-in-dependencies">Pulling in dependencies</a></h3>
<p>Again, not too dissimilar from using <a href="https://crates.io/">crates.io</a>, you can declare your dependencies
as normal with the exception that you need to specify the organisation the crate should
be pulled from provided you've declared the organisation in <code>.cargo/config.toml</code> as shown
in the previous section of this guide.</p>
<pre><code class="language-toml">[dependencies]
my-other-crate = { version = &quot;0.1&quot;, registry = &quot;my-organisation&quot; }
</code></pre>
<p>Your other Cargo dependencies from <a href="https://crates.io/">crates.io</a> can be declared as normal alongside
organisation dependencies.</p>
<div style="break-before: page; page-break-before: always;"></div><h1 id="chartered-guide"><a class="header" href="#chartered-guide">Chartered Guide</a></h1>
<ul>
<li><a href="guide/./config-reference.html">Configuration Reference</a></li>
</ul>
<div style="break-before: page; page-break-before: always;"></div><h1 id="configuration-reference"><a class="header" href="#configuration-reference">Configuration Reference</a></h1>
<p>An exhaustive list of all configuration values in chartered.</p>
<p>Configuration files are written in the TOML format, with simple key-value pairs inside of sections (tables). The following
is a quick overview of all settings, with detailed descriptions found below</p>
<h2 id="chartered-git"><a class="header" href="#chartered-git">chartered-git</a></h2>
<h3 id="configuration-format"><a class="header" href="#configuration-format">Configuration format</a></h3>
<pre><code class="language-toml">bind_address = &quot;127.0.0.1:2233&quot;
database_uri = &quot;postgres://user:password@localhost/chartered&quot; # can also be `sqlite://`
web_base_uri = &quot;http://localhost:8888/&quot;

[committer]
name = &quot;Chartered&quot;
email = &quot;noreply@chart.rs&quot;
message = &quot;Updated crates!&quot;
</code></pre>
<h3 id="configuration-keys"><a class="header" href="#configuration-keys">Configuration keys</a></h3>
<h4 id="bind_address"><a class="header" href="#bind_address"><code>bind_address</code></a></h4>
<ul>
<li>Type: string</li>
</ul>
<p>The IP address and port the web server should be bound to.</p>
<h4 id="database_uri"><a class="header" href="#database_uri"><code>database_uri</code></a></h4>
<ul>
<li>Type: string</li>
</ul>
<p>A connection string for the backing database, either <code>postgres</code> or <code>sqlite</code>, either in the
format of <code>postgres://user:password@localhost/chartered</code> (a <a href="https://www.postgresql.org/docs/9.4/libpq-connect.html#LIBPQ-CONNSTRING">postgres connection URI</a>),
<code>sqlite:///path/to/chartered.db</code> or <code>sqlite://:memory:</code>.</p>
<h4 id="web_base_uri"><a class="header" href="#web_base_uri"><code>web_base_uri</code></a></h4>
<ul>
<li>Type: string</li>
</ul>
<p>The path at which the Chartered API (<code>chartered-web</code>) is running. This should <em>always</em> be HTTPS when
running in production.</p>
<h4 id="committer"><a class="header" href="#committer"><code>committer</code></a></h4>
<p>The <code>committer</code> table defines the author of the commit that's sent to the
user.</p>
<h5 id="name"><a class="header" href="#name"><code>name</code></a></h5>
<ul>
<li>Type: string</li>
<li>Default: <code>chartered</code></li>
</ul>
<p>The name of the committer for any commits being created by <code>chartered-git</code>.</p>
<h5 id="email"><a class="header" href="#email"><code>email</code></a></h5>
<ul>
<li>Type: string</li>
<li>Default: <code>noreply@chart.rs</code></li>
</ul>
<p>The email address to list for the author of the commit pushed to the user</p>
<h5 id="message"><a class="header" href="#message"><code>message</code></a></h5>
<ul>
<li>Type: string</li>
<li>Default: <code>Update crates</code></li>
</ul>
<p>The commit message to use for any commits sent out.</p>
<hr />
<h2 id="chartered-web"><a class="header" href="#chartered-web">chartered-web</a></h2>
<h3 id="configuration-format-1"><a class="header" href="#configuration-format-1">Configuration format</a></h3>
<pre><code class="language-toml">bind_address = &quot;127.0.0.1:8080&quot;
database_uri = &quot;postgres://user:password@localhost/chartered&quot; # can also be `sqlite://`

storage_uri  = &quot;s3://s3-eu-west-1.amazonaws.com/my-cool-crate-store/&quot; # or file:///var/lib/chartered

[auth.password]
enabled = true # enables password auth 

[auth.&lt;provider&gt;] # openid connect provider
enabled = true
discovery_uri = &quot;https://gitlab.com/&quot;
client_id = &quot;[client-id]&quot;
client_secret = &quot;[client-secret]&quot;
</code></pre>
<h3 id="configuration-keys-1"><a class="header" href="#configuration-keys-1">Configuration keys</a></h3>
<h4 id="bind_address-1"><a class="header" href="#bind_address-1"><code>bind_address</code></a></h4>
<ul>
<li>Type: string</li>
</ul>
<p>The IP address and port the web server should be bound to.</p>
<h4 id="database_uri-1"><a class="header" href="#database_uri-1"><code>database_uri</code></a></h4>
<ul>
<li>Type: string</li>
</ul>
<p>A connection string for the backing database, either <code>postgres</code> or <code>sqlite</code>, either in the
format of <code>postgres://user:password@localhost/chartered</code> (a <a href="https://www.postgresql.org/docs/9.4/libpq-connect.html#LIBPQ-CONNSTRING">postgres connection URI</a>),
<code>sqlite:///path/to/chartered.db</code> or <code>sqlite://:memory:</code>.</p>
<h4 id="storage_uri"><a class="header" href="#storage_uri"><code>storage_uri</code></a></h4>
<ul>
<li>Type: string</li>
</ul>
<p>A URI in which crates should be stored, this can either be an <code>s3://</code> connection URI, or a local file path using
<code>file://</code>.</p>
<h4 id="authpassword"><a class="header" href="#authpassword"><code>[auth.password]</code></a></h4>
<p>The <code>[auth.password]</code> table controls the username/password-based authentication method.</p>
<h5 id="enable"><a class="header" href="#enable"><code>enable</code></a></h5>
<ul>
<li>Type: bool</li>
<li>Default: false</li>
</ul>
<p>Enables username/password-based authentication and registration.</p>
<h4 id="authprovider"><a class="header" href="#authprovider"><code>[auth.&lt;provider&gt;]</code></a></h4>
<p><code>[auth.&lt;provider&gt;]</code> tables represent an OpenID Connect provider that can be used to
login and register to the chartered instance. <code>&lt;provider&gt;</code> should not be changed once
set as the value is stored in the database along with users.</p>
<h5 id="enabled"><a class="header" href="#enabled"><code>enabled</code></a></h5>
<ul>
<li>Type: bool</li>
</ul>
<p>Enables the authentication provider, if this is disabled users will not be able to login
nor register using the provider.</p>
<h5 id="discovery_uri"><a class="header" href="#discovery_uri"><code>discovery_uri</code></a></h5>
<ul>
<li>Type: bool</li>
</ul>
<p>The OIDC Discovery URI that can be used to grab configuration for the provider, not including
<code>/.well-known/openid-configuration</code>.</p>
<h5 id="client_id"><a class="header" href="#client_id"><code>client_id</code></a></h5>
<ul>
<li>Type: string</li>
</ul>
<p>The Client ID given by the provider to identify the service.</p>
<h5 id="client_secret"><a class="header" href="#client_secret"><code>client_secret</code></a></h5>
<ul>
<li>Type: string</li>
</ul>
<p>The client secret given by the provider to authenticate the service.</p>

                    </main>

                    <nav class="nav-wrapper" aria-label="Page navigation">
                        <!-- Mobile navigation buttons -->
                        <div style="clear: both"></div>
                    </nav>
                </div>
            </div>

            <nav class="nav-wide-wrapper" aria-label="Page navigation">
            </nav>

        </div>

        <script type="text/javascript">
            window.playground_copyable = true;
        </script>
        <script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="searcher.js" type="text/javascript" charset="utf-8"></script>
        <script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
        <script src="highlight.js" type="text/javascript" charset="utf-8"></script>
        <script src="book.js" type="text/javascript" charset="utf-8"></script>

        <!-- Custom JS scripts -->
        <script type="text/javascript">
        window.addEventListener('load', function() {
            window.setTimeout(window.print, 100);
        });
        </script>
    </body>
</html>