# le-store-fs A greenlock keypair and certificate storage strategy with wildcard support (simpler successor to le-store-certbot). # Usage ```js var greenlock = require('greenlock'); var gl = greenlock.create({ configDir: '~/.config/acme' , store: require('le-store-fs') , approveDomains: approveDomains , ... }); ``` # File System The default file system layout mirrors that of le-store-certbot in order to make transitioning effortless, in most situations: ``` acme ├── accounts │   └── acme-staging-v02.api.letsencrypt.org │   └── directory │   └── sites@example.com.json └── live ├── example.com │   ├── bundle.pem │   ├── cert.pem │   ├── chain.pem │   ├── fullchain.pem │   └── privkey.pem └── www.example.com ├── bundle.pem ├── cert.pem ├── chain.pem ├── fullchain.pem └── privkey.pem ``` # Wildcards & AltNames Working with wildcards and multiple altnames requires greenlock >= v2.7. To do so you must set `opts.subject` and `opts.domains` within the `approvedomains()` callback. `subject` refers to "the subject of the ssl certificate" as opposed to `domain` which indicates "the domain servername used in the current request". For single-domain certificates they're always the same, but for multiple-domain certificates `subject` must be the name no matter what `domain` is receiving a request. `subject` is used as part of the name of the file storage path where the certificate will be saved (or retrieved). `domains` should be the list of "altnames" on the certificate, which should include the `subject`. ## Simple Example ```js function approveDomains(opts, certs, cb) { // foo.example.com => *.example.com var wild = '*.' + opts.domain.split('.').slice(1).join('.'); if ('*.example.com' !== wild) { cb(new Error(opts.domain + " is not allowed")); } opts.subject = '*.example.com'; opts.domains = ['*.example.com']; cb({ options: opts, certs: certs }); } ``` ## Realistic Example ```js function approveDomains(opts, certs, cb) { var related = getRelated(opts.domain); if (!related) { cb(new Error(opts.domain + " is not allowed")); }; opts.subject = related.subject; opts.domains = related.domains; cb({ options: opts, certs: certs }); } ``` ```js function getRelated(domain) { var related; var wild = '*.' + domain.split('.').slice(1).join('.'); if (Object.keys(allAllowedDomains).some(function (k) { return allAllowedDomains[k].some(function (name) { if (domain === name || wild === name) { related = { subject: k, altnames: allAllowedDomains[k] }; return true; } }); })) { return related; } } ``` ```js var allAllowedDomains = { 'example.com': ['example.com', '*.example.com'] , 'example.net': ['example.net', '*.example.net'] } ```