Hi Aaron
I missed your reply in the rush…
This is my sync_gateway.json:
{
"maxFileDescriptors": 250000,
"logging": {
"console": {
"color_enabled": true,
"log_keys": ["HTTP+", "Sync"]
}
},
"adminInterface": "0.0.0.0:4985",
"interface": "0.0.0.0:4984",
"databases": {
"data": {
"use_views":false,
"num_index_replicas":0,
"bucket": "data",
"server": "http://db1,db2:8091",
"username": "remoteUser",
"password": "........",
"enable_shared_bucket_access": true,
"import_docs": true,
"users": { "GUEST": { "disabled": true, "admin_channels": ["!"] } },
"import_filter": `
function(doc) {
// Some document types not allowed on mobile
if (doc.type == 'EnvLake' || doc.type == 'EnvMeasurement' || doc.type == 'ActivityLog' || doc.type == 'Feedback' || doc.type == 'Image') {
return false;
}
return true;
}
`,
"sync": `
function (doc, oldDoc) {
function _log(t) {
// Write to sg_info.log
console.log('' + t);
}
function _getUserKey(d) {
var key = null;
if (d) {
if (d.type == 'User') {
key = d.key;
} else {
key = d.userkey;
}
}
return key;
}
if (doc && doc._deleted) {
// Doc. deleted -> if public then require
if(oldDoc){
var userkey = _getUserKey(oldDoc);
_log('delete doc id: ' + doc._id + ', userkey=' + userkey);
if (userkey != null) {
requireUser(userkey);
} else {
requireAdmin();
}
}
_log('doc deleted, id: ' + (doc._id || 'no id!') + ', ' + (oldDoc ? ('old key=' + oldDoc.key + ', userkey=' + userkey) : 'no oldDoc'));
return;
}
_log('doc id: ' + (doc._id || 'no id!') + ', ispublic: ' + doc.ispublic + ', userkey=' + doc.userkey + ', ' + (oldDoc ? (oldDoc._deleted ? 'oldDoc is deleted' : ('old key=' + oldDoc.key + ', oldDoc.userkey=' + oldDoc.userkey + ' update')) : ' creation'));
// Document type is mandatory
if (typeof doc.type === 'undefined') {
_log('Document type missing: ' + JSON.stringify(doc));
throw ({ forbidden: "Document type is required. id=" + doc._id });
}
if (doc.type == 'EnvLake' || doc.type == 'EnvMeasurement') {
throw ({ forbidden: "Document type '" + doc.type + "' not allowed on mobile. id=" + doc._id });
}
// Avoid sync'ing new fishery types
if (typeof doc.fisherytype !== 'undefined' && doc.fisherytype !== '1') {
throw ({ forbidden: "Fishery type: '" + doc.fisherytype + "' not allowed on mobile. Document type '" + doc.type + "', id=" + doc._id });
}
// Document key is mandatory
if (typeof doc.key === 'undefined') {
_log('Document key missing: ' + JSON.stringify(doc));
throw ({ forbidden: "Document key is required. id=" + doc._id });
}
// Update: Cannot allow change of type or key
if (oldDoc != null && !oldDoc._deleted) {
// Update
if (oldDoc.type != doc.type) {
throw ({ forbidden: "Can't change doc type" });
}
if (oldDoc.key != doc.key) {
throw ({ forbidden: "Can't change doc key" });
}
}
// Document sync is disabled (used for type Image - but generic implementation)
if (doc.issyncdisabled) {
throw ({ forbidden: "Sync. disabled for id=" + doc._id });
}
var includeForStats = false;
var userkey = _getUserKey(doc);
// All public docs (not deleted) are available in the app
if (doc.ispublic && doc.deleted != true) {
_log('public, id: ' + (doc._id || 'no id!'));
channel('!');
} else if (doc.type == 'FishingTrip' || doc.type == 'Catch') {
// All fishing trips and catches are available (for stats and quotas)
includeForStats = (userkey == null);
_log('Trip/catch for user: ' + (doc.userkey || "<external>") + ', id: ' + (doc._id || 'no id!'));
channel('!');
}
// All users are available (for stats)
// 2021.04.07/Jda - replaced by Summary doc.
// if (doc.type == 'User' && doc.deleted != true) {
// _log('User doc, id: ' + (doc._id || 'no id!'));
// channel('!');
// }
// Allow anyone to create a Feedback or Observation on the server
if (oldDoc == null && userkey == null && (doc.type == 'Feedback' || doc.type == 'Observation')) {
_log('Created ' + doc.type + ': ' + (doc._id || 'no id!') + ', key: ' + doc.key + ' as anonymous user ');
return;
}
// Allow app user to create an ActivityLog on the server
if (oldDoc == null && doc.type == 'ActivityLog') {
_log('Created ' + doc.type + ': ' + (doc._id || 'no id!') + ', key: ' + doc.key + ' for user: ' + doc.userkey);
return;
}
// Only non-public docs "owned" by user can be created/updated (and replicated)
if (userkey != null) {
if (oldDoc != null && ! oldDoc._deleted) {
// Update
if (oldDoc.userkey && oldDoc.userkey != doc.userkey) {
throw ({ forbidden: "Can't change user key" });
}
}
_log('User owned, id: ' + (doc._id || 'no id!') + ', type: ' + doc.type + ', user: ' + userkey);
if(doc.type != 'Image'){ // Do not send images TO mobile
channel('channel.' + userkey);
}
access(userkey, 'channel.' + userkey);
requireUser(userkey);
} else if (doc.ispublic || includeForStats) {
requireAdmin();
} else {
// Creation/update without user
_log('Document type cannot be created without user key: ' + (doc.type === 'Image' ? doc._id : JSON.stringify(doc)));
throw ({ forbidden: "This document type cannot be created without user key. id=" + doc._id });
}
}
`,
"allow_conflicts": false,
"revs_limit": 20
}
}
}
Server is now on 7.1.1 (CE) and sync.gateway 2.8.3. I see that there is a new sync gateway - but I also see quite some changes so I have not dared (!) to migrate over yet. There will need to be some advantages for the effort 
One thing I observed is that it seem to be more complicated to edit the sync. formulas as that will have to be done via API calls - is this really correct or did I misunderstand something??