Hi all, I have a first RFC up here: https://github.com/apache/couchdb-documentation/pull/424
The goal of this is to have a concrete scope of changes written down and have it be a minimal set to implement while being useful. Specifically, this proposal defers to later: * per-access views * differentiation between read and write access for documents * sharing individual documents between multiple users or groups The feature design so far aims to allow the addition of the above at a later point. Specifically, per-access views and read/write differentiation should not be too hard to add, but doc sharing might better be left for a FoundationDB future. I’d like a thorough review and comments that specifically address any holes in the proposal. In particular, I’m interested to hear if there are any showstoppers in there that a common db-per-user setup could not migrate over to this model. Best Jan — > On 4. Apr 2019, at 10:45, Jan Lehnardt <j...@apache.org> wrote: > > Thanks for your initial comments. > >> On 3. Apr 2019, at 23:07, Adam Kocoloski <kocol...@apache.org> wrote: >> >> I’m also in favor of dropping Scenario 3. >> >> One topic we may have discussed in the past but I wanted to close out here: >> in the relational database world it’s not uncommon to use materialized views >> as an access control mechanism to selectively expose contents of a table to >> clients who cannot access the table directly. Does the current thinking on >> _access for views support that use case? Can we build a view using a set of >> roles inherited from the user who created the design doc, but then turn >> around and set the _access on the view itself to a less-restrictive set? > > 3 minutes thinking it over didn’t reveal any particular problems with this > feature, aside from include_docs not working as expected, which might be an > okay trade-off for now. But could be included later. > > >> On the _revs_diff topic — I’m not all that concerned about users trying to >> guess revision IDs that exist on the server, and then reverse-engineer the >> contents of the existing revisions. Maybe I ought to be. > > I’m not particularly worried, but it is at least a theoretical situation > where our user’s can be caught with their pants down when they didn’t expect > it. All I want to make sure is to document this properly. C.f. git where if > you get access to a repo, you get the whole history, not just the state from > where you started having access. > > Best > Jan > — > >> >> On a somewhat-related note, I have had conversations before with folks who >> are keen to adopt these sorts of fine-grained access control systems who >> said they actually prefer to have a 403 Forbidden response list the set of >> privileges that would be sufficient to access the resource. I found this >> surprising, but I guess it comes down to a user needing to figure out what >> kind of security exception to apply for in order to make progress with some >> data analysis. I think this is a topic on which we could make a fairly >> late-binding decision — or even have it as a configurable option. >> >> I could definitely see the base Scenario 1 (single _access labels) landing >> ahead of the more-complex sharing models. >> >> I haven’t had a chance to take a deep look at the code but the design seems >> good and thoughtful, and I definitely like the focus on the use cases. >> >> Adam >> >>> On Mar 14, 2019, at 11:21 AM, Jan Lehnardt <j...@apache.org> wrote: >>> >>> My replies now inline. >>> >>>> On 14. Mar 2019, at 16:13, Jan Lehnardt <j...@apache.org> wrote: >>>> >>>> I received some notes privately from Gregor Martynus, which I’m >>>> reproducing here in email thread form. This email is all Gregor’s notes, >>>> my next email is my replies to them. >>>> >>>>> On 10. Mar 2019, at 15:51, Jan Lehnardt <j...@apache.org> wrote: >>>>> >>>>> Hey all, >>>>> >>>>> after mulling this over some more, I’d like to tackle the detailed API >>>>> and behaviour for this. Especially how _access work in conjunction with >>>>> existing access control features. >>>>> >>>>> My guiding principles so far are: >>>>> >>>>> 1. Make the API intuitive, things should work like they look like they >>>>> should work like. >>>>> 2. The default should never be that a resources is accidentally left >>>>> accessible to the public. >>>>> 3. This should work as a natural extension to the existing security >>>>> features*. >>>>> >>>>> * I’d be up for reworking the whole lot, too, but that might be a better >>>>> discussion for > 4.0. >>>>> >>>>> >>>>> ## Database Creation and Default Behaviours >>>>> >>>>> Creating a database with _access features is, as mentioned before done >>>>> via a flag to PUT /database?access=true >>>>> >>>>> In a 3.0 world where this would land, we already agreed that databases >>>>> should be admin-only by default (instead of world read/writeable today). >>>>> This is a sensible default, but that leaves us with an _access enabled >>>>> database that can’t be used by anyone by server or db admins. Not very >>>>> useful. >>>>> >>>>> To allow arbitrary users to use the db, I suggest we use the existing >>>>> _security system: i.e. if a user or a group a user belongs to is >>>>> mentioned in either `admins` or `members` inside of _security, they can >>>>> proceed and create documents on the db. This puts a second step burden on >>>>> the application developer, but it slots cleanly into the existing >>>>> security mechanisms, and doesn’t require special case handling. >>>>> Alternatively, we could define that _security isn’t available in _access >>>>> enabled databases, but that’s something I’d like to avoid if at all >>>>> possible. >>>>> >>>>> In order to make it easy to specify that “everyone in _users” should be >>>>> able to use the db, I suggest we add a new role `_users` that is valid >>>>> inside _security, which means “everyone in /_users” (this only excludes >>>>> server admins which have full access anyway). >>>>> >>>>> * * * >>>>> >>>>> >>>>> ## Document Creation and Access Control >>>>> >>>>> Next, one of our non-admin users creates a doc. There are multiple >>>>> options as to how we store the _access information. >>>>> >>>>> 1. Automatically translate the userCtx.name of a doc creation (not an >>>>> update) into the first element of the _access array. E.g. user_a PUT >>>>> /db/doc {"a":1} creates this doc: {"a":1,"_access":["user_a"]}. This is a >>>>> little bit counter-intuitive. >>>>> >>>>> 2. We require that a user puts "_access":["user_a"] in themselves. This >>>>> is an explicit granting of access permissions on doc creation and I think >>>>> is preferable. >>>> >>>> I prefer being explicit. >>>> >>>> >>>>> >>>>> This leaves the edge case of docs that have no _access member: so far I >>>>> thought those docs are admin-only, with maybe a db-wide option to swap >>>>> the default to public access, but I think given the explicitness of 2. we >>>>> can do better: require _access for all new doc creations in >>>>> access-enabled databases. A user can not create a new document without an >>>>> _access field that is an array that has at least one member. For public >>>>> documents, we could invent a new role _public, and admin-only docs could >>>>> use the existing role _admin. >>>>> >>>>> The one downside to this approach is that we won’t be able to replicate >>>>> existing databases into an access-enabled database without modifying all >>>>> documents. This might be a worthwhile trade-off, but we should make that >>>>> decision consciously and document it well. >>>> >>>> We could also provide tooling for migrations? >>> >>> I’d love tooling, but we’d have to make sure we can do it correctly for a >>> big number of use-cases. For the acceptance of this change, I’d make >>> “documenting a migration path for db-per-user setups” a MUST have, and any >>> code that helps with that a nice to have. >>> >>>> >>>> >>>>> We could allow for a special case where an _admin user can create docs >>>>> that have no _access field, and those docs are treated as having only the >>>>> _admin role in _access. So at least we could replicate all data in, but >>>>> then require a manual step to update all docs to say, migrate an existing >>>>> db-per-user app, while not accidentally exposing any docs to folks that >>>>> shouldn’t read them. >>>>> >>>>> For the rest of cRUD, the existing document must store one of the RUD-ing >>>>> user’s name or role in its _access field. >>>>> >>>>> For both creations and updates, a user MUST supply at least one role they >>>>> belong to or their own username. >>>>> >>>>> * * * >>>>> >>>>> >>>>> ## _revs_diff >>>>> >>>>> /db/_revs_diff can answer the question of which revisions of a document >>>>> do NOT exist on a replication target: >>>>> http://docs.couchdb.org/en/stable/api/database/misc.html#db-revs-diff >>>>> >>>>> This would allow users to specify ids and rev(s) for docs they don’t have >>>>> access too (anymore), so the result schema should be expanded to handle >>>>> id: unauthorized or somesuch, something the replicator needs to know what >>>>> to do with, if it encounters it (say a user got removed from the _access >>>>> list inbetween the replicator opening _changes and requesting the doc). >>>>> >>>>> The _revs_diff implementation would have to altered to send an >>>>> unauthorized token for each doc the requesting userCtx has no access to. >>>>> If we can re-use some of our existing indexes, or any other performance >>>>> optimisation, that’d be great. I haven’t looked at that code at all, yet. >>>>> >>>>> An important side-effect of this is, once a user has been added to a >>>>> doc’s _access list, they get access to “the full history of the doc”, >>>>> even before they had access. Of course, in CouchDB this means only >>>>> getting access to the rev ids, and not the content, but since they are >>>>> content-addressable hashes, a user could brute-force themselves into >>>>> revealing certain real values from earlier incarnations of the doc. I’d >>>>> rather not track _access per document revision in perpetuity, so this is >>>>> something we have to be very up-front about. >>>>> >>>>> * * * >>>>> >>>>> >>>>> ## Partitioned Databases >>>>> >>>>> I mentioned partitioned databases in my previous mail, and I think it is >>>>> something we can document that end-users can opt into, but doesn’t >>>>> require any special casing on the _access proposal. That is, if users >>>>> start prefixing their doc ids with a user name or id and enable both >>>>> _access and partitions, then they get all the benefits of a partitioned >>>>> database, and if they choose not to, they don’t, but things keep working. >>>>> There are enough use-cases to warrant both behaviours. >>>>> >>>>> * * * >>>>> >>>>> >>>>> ## Scenarios that _access should help with. >>>>> >>>>> Overall, we developed _access to allow users to stop using the >>>>> db-per-user architecture, but once we have per-doc-access control, folks >>>>> might start using this for all manner of things. We should be clear about >>>>> which scenarios we support and which we don’t. >>>>> >>>>> >>>>> ### Scenario 1: db-per-user >>>>> >>>>> In this scenario, _access enabled databases, the only way to allow >>>>> mutually untrusting users to store data in a part of CouchDB that only >>>>> they (and admins) have access to was giving each user their own database. >>>>> >>>>> In an _access enabled database, users can >>>>> CRUD/_changes/_all_docs/_revs_diff their own docs knowing no other user >>>>> (aside from admins) can access those docs. >>>>> >>>>> This is the simplest scenario, as all we’d have to track the owner of a >>>>> document and produce by-access-id/seq indexes based on that owner. >>>>> >>>>> The current prototype implementation mostly reflects this stage. Not >>>>> saying this is what we should ship, but it is the easiest do implement >>>>> and explain. >>>>> >>>>> Aside, I might be able to be persuaded to ship this as a 2.x feature, to >>>>> help those folks who don’t need anything else. >>>>> >>>>> >>>>> ### Scenario 2: db-per-user + Sharing >>>> >>>> One scenario we should address is how stopping to share would work when >>>> documents are continuously replicated, e.g. to a client for offline usage. >>>> My understanding is that for the person who’s access to documents got >>>> revoked does not get _changes update telling them that their access got >>>> removed, it would be up to the application developer to implement some >>>> kind of "notification" meta documents. Unless you have a better idea? >>> >>> Since we now have a purge API as well, we could treat an un-share as a >>> purge for clients, and they can decide what to do with it. >>> >>> Alternatively, we need to make breaking changes to _changes feed, maybe we >>> can hide that behind an opt-in flag, like “/db/_changes?access=true”, and >>> then we can send new rows like: >>> >>> {seq: XYZ, id: abc, rev:4-YYY, _revoked: true} or somesuch. >>> >>> >>>> >>>>> >>>>> The second we allow per doc auth, users will want to share those docs >>>>> with other users. That’s why we initially suggested the _access field be >>>>> an array, so other users and groups can be specified to have access. >>>>> There are multiple scenarios in this one alone: >>>>> >>>>> #### 2.1: The Todo List >>>>> >>>>> In this scenario, a user has a reasonable amount of ”personal data” that >>>>> they want to selectively share with one or more other users. >>>>> >>>>> #### 2.2: The Chat/Forum/Newsgroup >>>>> >>>>> In this scenario, a user wants to share any number of documents with a >>>>> reasonable number of groups. However, since we need to limit the number >>>>> of groups a user belongs to (currently 10, see below for details), this >>>>> might actually not be a great solution. Or folks couldn’t be in more than >>>>> 10 chat groups at a time. >>>>> >>>>> #### 2.3: The Corporate Hierarchy >>>>> >>>>> In this scenario, users want to share any number of docs with a >>>>> reasonable number of groups in a top-down/bottom-up fashion. Think CEO >>>>> shares with executives, execs share with divisions, divisions report up >>>>> to their one executive, etc. >>>>> >>>>> >>>>> ### 3: Multiple Apps >>>>> >>>>> The preceding scenarios all assume that a single application is >>>>> responsible for everything. However, once we allow mutually distrusting >>>>> users into a single database *and* make each per-user slice work (almost) >>>>> like a full standalone CouchDB database, what would stop users from using >>>>> this for a multi-homing feature, where different applications are used >>>>> for each user in the same database? >>>>> >>>>> I’ll be referring to these scenarios down the line. >>>>> >>>>> * * * >>>>> >>>>> >>>>> ## Design Docs >>>>> >>>>> ### Admin >>>>> >>>>> One of the downsides of db-per-user is managing design docs in the face >>>>> of a changing application, that is, how to distribute new design docs >>>>> across 10s of 1000+s of user dbs? It’s not impossible, but tedious. In >>>>> all scenarios above but scenario 3., we could simplify this >>>>> significantly. Say an admin creates a design doc, and gives all users in >>>>> the db access to this design doc (this could be with the _users role, or >>>>> yet another new role _members, if we need it), requesting the result of a >>>>> view defined in that design doc will produce an index that is powered by >>>>> the requesting user’s by-access-seq index section(s). >>>>> >>>>> N.B., this would require us to change a fundamental assumption when doing >>>>> the association between a design doc’s definition and index: normally, >>>>> there is only the `views` member that is hashed and that hash is used as >>>>> the index’s filename. Because there is only by-seq to power a view, that >>>>> all works. But now that we have an arbitrary set of sections on >>>>> by-access-seq, any view index built will have to take a user’s name and >>>>> roles into account. When a user leaves a group, or gains a group, all >>>>> indexes for that user will no longer be valid and need rebuilding. >>>>> >>>>> >>>>> ### User >>>>> >>>>> In any of the scenarios above, but especially 3., there could be >>>>> legitimate per-user design docs, so how should those be treated in an >>>>> _access enabled database? >>>>> >>>>> The significant fields in a design doc are `views`, `validate_doc_update` >>>>> and `filters` (I’ll skip over the deprecated _show, _list, and _update). >>>>> >>>>> The easiest to handle is a `filters`: if a user specifies a filter for a >>>>> _changes request or replication that lives in a design doc they don’t >>>>> have access to, they get an error, similar to if they specify a >>>>> non-existent design doc, just with `unauthorized` instead of `not_found`. >>>>> >>>>> Next `views` is also not very hard to imagine working: just like globally >>>>> defined views for that db, the index is built for each user based on the >>>>> user’s name and roles. >>>>> >>>>> More troubling are `validate_doc_update` functions: One, they are already >>>>> troubling in that they slow down any document updates. Two, if we now >>>>> import an existing db-per-user scenario where each user has their own >>>>> design docs, >>>> >>>> I can’t think of a db-per-user scenario where each user DB would have a >>>> different validate_doc_update method? It would be the same method with >>>> access to the user context, the DBs security setting and the document, so >>>> it would act differently for different users, but using the same code. >>> >>> They wouldn’t be different, but if we were do replicate 1000 db-per-user >>> design docs into a single database, as per today’s semantics, we’d have to >>> run 1000 VDUs on each doc update. >>> >>>> >>>>> how should we apply validate_doc_update functions? 10s of 1000s of VDUs >>>>> are impractical to apply on each doc update, let alone just the >>>>> management of VDUs that are active on a database. One option would be to >>>>> ignore VDUs if they are not defined globally (say with a _members role). >>>>> But especially in scenario 3. this becomes problematic, but even without >>>>> that specific scenario, this violates the no surprises best practice. >>>>> >>>>> We could say: >>>>> >>>>> a) we don’t support scenario 3. >>>> >>>> +1, I think it would make our lives easier in general if we don’t >>>> recommend to share the same CouchDB for multiple apps. At least I don’t >>>> see a reason to do that at this point. >>> >>> I think I like this best, too, but I’d like to hear from others as well. >>> >>> >>> Best >>> Jan >>> — >>>> >>>>> b) we find a complicated but efficient way to apply only those VDUs that >>>>> are defined in design docs the writing user has access to plus any global >>>>> ones (this would be neat but rather complicated and potentially still >>>>> impractical from a performance perspective for N users). >>>>> c) we could store all per-user design docs, but ignore them completely, >>>>> VDUs, views and filters. >>>>> >>>>> I think I currently fall on the side of not supporting scenario 3. and >>>>> asking folks who migrate db-per-user to de-duplicate design docs and keep >>>>> them per-app. I believe that is a good trade-off between the most common >>>>> scenarios for db-per-user while keeping the implementation manageable. >>>>> Globally accessible design docs would show up in a user’s changes feed >>>>> and would replicate down to say a PouchDB application which might be the >>>>> exclusive user of those design docs. >>>>> >>>>> In practice this would mean, a document that has an _id that starts with >>>>> _design/ will have to be produced by a database admin. Luckily, that’s >>>>> already the case. We should just make sure that folks don’t give db-admin >>>>> access to all users habitually. >>>>> >>>>> >>>>> ## Read and Write Access >>>>> >>>>> Speaking of validate_doc_update, it is used for two things: checking >>>>> document schema and doc update authorisation. >>>>> >>>>> Once we allow access to a document with an _access field, we need to >>>>> decide what kind of access this gives to a doc: read-only or read-write >>>>> (I’m not considering write-only because for anything but doc creations >>>>> this is not useful as you need access to the current _rev). >>>>> >>>>> However, when we look at implementing an application on top of our >>>>> existing API, it is already weird that read access can be controlled >>>>> globally (or with _access on a per doc level), but write access requires >>>>> writing JavaScript code. I think it would be a reasonable expectation for >>>>> users to expect a per-doc read/write permission granting. >>>> >>>> Yes! >>>> >>>>> >>>>> So we could have all of the above, but with two extra fields: >>>>> _access_read and _access_write, or _access: {read: [], write: []} >>>> >>>> I prefer this API for its compactness, thinking about offline >>>> synchronization. The smaller the docs, the better. >>>> >>>> Best >>>> “Gregor” >>>> — >>>> >>>> >>>>> or we overload user and group names: _access: [user_a:read, user_b:write] >>>>> (or any permutation thereof). Overloading can cause trouble with >>>>> naturally occurring characters in group names. >>>>> >>>>> The former seems more explicit, but from an API perspective that’s a >>>>> little more awkward: remember that we currently have an arbitrary limit >>>>> of 10 members in a user’s role array, to avoid excessive fan out on >>>>> cluster-internal operations. Partitioned dbs could get away with more, >>>>> more easily however. If we allow the specification of access control in >>>>> two lists, and one of the lists implies membership in the other, we have >>>>> a total limit of 10 members across both arrays. Or we limit 5 + 5, but >>>>> that seems excessive, while 10 total seems weird, but doable. Anyway, >>>>> good bikeshed. >>>>> >>>>> >>>>> * * * >>>>> >>>>> >>>>> So far. I think all of the problems outlined are solvable, if with a >>>>> clear definition of what use-cases we do not support with access. If you >>>>> have more scenarios than the ones I outlined, please add them and we can >>>>> see if they cause any additional trouble. >>>>> >>>>> Thanks for reading this far and I’m looking forward to your feedback. >>>>> >>>>> >>>>> Best, >>>>> Jan “_access” Lehnardt >>>>> — >>>>> >>>>> >>>>> >>>>> >>>>>> On 17. Feb 2019, at 15:25, Jan Lehnardt <j...@apache.org> wrote: >>>>>> >>>>>> Hi Everyone, >>>>>> >>>>>> I’m happy to share my work in progress attempt to implement the per-doc >>>>>> access control feature we discussed a good while ago: >>>>>> >>>>>> https://lists.apache.org/thread.html/6aa77dd8e5974a3a540758c6902ccb509ab5a2e4802ecf4fd724a5e4@%3Cdev.couchdb.apache.org%3E >>>>>> >>>>>> <https://lists.apache.org/thread.html/6aa77dd8e5974a3a540758c6902ccb509ab5a2e4802ecf4fd724a5e4@%3Cdev.couchdb.apache.org%3E> >>>>>> >>>>>> You can check out my branch here: >>>>>> >>>>>> https://github.com/apache/couchdb/compare/access?expand=1 >>>>>> <https://github.com/apache/couchdb/compare/access?expand=1> >>>>>> >>>>>> It is very much work in progress, but it is far enough along to warrant >>>>>> discussion. >>>>>> >>>>>> The main point of this branch is to show all the places that we would >>>>>> need to change to support the proposal. >>>>>> >>>>>> Things I’ve left for later: >>>>>> >>>>>> - currently only the first element in the _access array is used. Our >>>>>> and/or syntax can be added later. >>>>>> - building per-access views has not been implemented yet, couch_index >>>>>> would have to be taught about the new per-access-id index. >>>>>> - pretty HTTP error handling >>>>>> - tests except for a tiny shell script 😇 >>>>>> >>>>>> Implementation notes: >>>>>> >>>>>> You create a database with the _access feature turned on like so: PUT >>>>>> /db?access=true >>>>>> >>>>>> I started out with storing _access in the document body, as that would >>>>>> allow for a minimal change set, however, on doc updates, we try hard not >>>>>> to load the old doc body from the database, and forcing us to do so for >>>>>> EVERY doc update under _access seemed prohibitive, so I extended the >>>>>> #doc, #doc_info and #full_doc_info records with a new `access` attribute >>>>>> that is stored in both by-id and by-seq. I will need guidance on how >>>>>> extending these records impact multi-version cluster interop. And >>>>>> especially whether this is an acceptable approach. >>>>>> >>>>>> https://github.com/apache/couchdb/compare/access?expand=1&ws=0#diff-904ab7473ff8ddd07ea44aca414e3a36 >>>>>> >>>>>> * * * >>>>>> >>>>>> The main addition is a new native query server called >>>>>> couch_access_native_proc, which implements two new indexes by-access-id >>>>>> and by-access-seq which do what you’d expect, pass in a userCtx and >>>>>> retrieve the equivalent of _all_docs or _changes, but only including >>>>>> those docs that match the username and roles in their _access property. >>>>>> The existing handlers for _all_docs and _changes have been augmented to >>>>>> use the new indexes instead of the default ones, unless the user is an >>>>>> admin. >>>>>> >>>>>> https://github.com/apache/couchdb/compare/access?expand=1&ws=0#diff-fbb53323f07579be5e46ba63cb6701c4 >>>>>> >>>>>> * * * >>>>>> >>>>>> The rest of the diff is concerned with making document CRUD behave as >>>>>> you’d expect it. See this little demonstration for what things look like: >>>>>> >>>>>> https://gist.github.com/janl/b6d3f7502aa20b7b9ab9d9dcb8e92497 >>>>>> <https://gist.github.com/janl/b6d3f7502aa20b7b9ab9d9dcb8e92497> (I’m >>>>>> just noticing that there might be something wonky with DELETE, but >>>>>> you’ll get the gist #rimshot) >>>>>> >>>>>> * * * >>>>>> >>>>>> Open questions: >>>>>> >>>>>> - The aim of this is to get as close to regular CouchDB behaviour as >>>>>> possible. One thing that is new however which would require all apps to >>>>>> be changed is that for an _access enabled database to include an _access >>>>>> field in their docs (docs with no _access are admin-only for now). We >>>>>> might want to consider on new document writes to auto-insert the >>>>>> authenticated user’s name as the first element in the _access array, so >>>>>> existing apps “just work”. >>>>>> >>>>>> - Interplay with partitioned dbs: eschewing db-per-user is already a >>>>>> large boon if you have a lot of users, but making those per-user >>>>>> requests inside an _access enabled database efficient would be doubly >>>>>> nice, so why not use the username from the first question above and use >>>>>> that as the partition key? This would work nicely for natural users with >>>>>> their own docs that want to share them with others later, but I can >>>>>> easily imagine a pipelined use of CouchDB, where a “collector” user >>>>>> creates all new docs, an “analyser” takes them over and hand them to a >>>>>> “result” user for viewing. In that case, we’d violate the >>>>>> high-cardinality rule of partitions (have a lot of small ones), instead >>>>>> all docs go through all three users. I’d be okay with treating the later >>>>>> scenario as a minor use-case, but for that use-case, we should be able >>>>>> to disable auto-partitioning on db creation. >>>>>> >>>>>> - building access view indexes for docs that have frequent _access >>>>>> changes, lead to many orphaned view indexes, we should look at an >>>>>> auto-cleanup solution here (maybe keep 1-N indexes in case folks just >>>>>> swap back and forth). >>>>>> >>>>>> * * * >>>>>> >>>>>> I’ll leave this here for now, I’m sure there are a few more things to >>>>>> consider. >>>>>> >>>>>> I’d love to hear any and all feedback you might have. Especially if >>>>>> anything is unclear. >>>>>> >>>>>> Best >>>>>> Jan >>>>>> — >>>>> >>>>> -- >>>>> Professional Support for Apache CouchDB: >>>>> https://neighbourhood.ie/couchdb-support/ >>>>> >>>> >>>> -- >>>> Professional Support for Apache CouchDB: >>>> https://neighbourhood.ie/couchdb-support/ >>>> >>> >>> -- >>> Professional Support for Apache CouchDB: >>> https://neighbourhood.ie/couchdb-support/ >>> <https://neighbourhood.ie/couchdb-support/> > > -- > Professional Support for Apache CouchDB: > https://neighbourhood.ie/couchdb-support/ > -- Professional Support for Apache CouchDB: https://neighbourhood.ie/couchdb-support/